基于 Jmeter 的轻量级云压测平台的原理与实现 :压测引擎

2023-11-04

目录

前言:

平台的技术

平台的初衷

平台从开源开始到现在拥有了一些核心的功能:

印象深刻的技术点:

为什么执着于 Jmeter-API

平台能带来什么

压测引擎

前端入口

Controller

必要的 Jmeter 配置准备

对 Jmeter 脚本的必要加载操作

单机执行脚本

分布式执行脚本

最后


前言:

JMeter 是一个广泛使用的性能测试工具,它支持许多不同的测试技术和方法。其中,云压测是一种常用的测试方法,它可以使用云计算资源来实现大规模的测试和压力测试。 

平台的技术

平台的初衷

平台的核心初衷很简单,就是能在浏览器中完成一系列 Jmeter 操作,包括启停脚本,在线监控,在线报告。
但是想一下,其实 Jmeter 的核心初衷貌似更简单:“load test functional behavior and measure performance”,大意是能进行性能测试并且查看监控结果。
结果 Jmeter 的代码量突破了 67 万行。
平台中我的代码突破了 8000 行。
稍微感慨啊,我本来合计 “小而美” 搞定的,看来初衷和代码行数真的不成正比。

平台从开源开始到现在拥有了一些核心的功能:

  • 启动脚本,包括 Jmeter-API 的启动和调用 Jmeter-Home 的拼装脚本启动,包括单机节点启动和分布式启动。
  • 全部停止脚本和单独停止脚本,单机和分布式情况下都适用。
  • 支持平台内适用 Jmeter-API 的方式同时启动多脚本,同时监控的数据是正确的。
  • Jmeter-API 启动脚本时,支持 Jmeter 自带的函数,同时支持更多的 Jmeter 的常用 sampler。
  • 支持 Jmeter-API 方式的脚本调试,在线也能看脚本的效果和问题。
  • 参数化文件支持自动同步到各个节点机。
  • 性能/调试报告的异步生成及下载。
  • 系统空间的控制,支持不生成测试报告和保留报告而仅删除测试结果数据。

印象深刻的技术点:

  • shiro 的配置及权限控制。
  • spring-boot 读取配置文件及 Controller——Service——DAO——Mapper 的各种操作。
  • 子类的方式重写 Jmeter 的源码方法。
  • javassist 字节码修改方式重写 Jmeter 源码。
  • 观察者模式重写 Jmeter 调用脚本的各种监听器。
  • classLoader 的实际运用和 static 代码块。
  • Java 内调用命令行和各种回调。
  • 异步线程池方式实现 xsl 模板生成 html 报告。
  • 文件上传下载。
  • 前端监控的定时触发,数据在内存中如何对压测机性能影响最小,内存和数据库。
  • 前端监控的数据如何计算得到,尤其是分布式这些数据要怎么处理。
  • Echarts 的写法和调试。
  • 数据库表的设计,配置项的设计。

然后我发现自己面对所有这些技术问题,解决的速度是很快的,我觉得自己是战无不胜的。

issue 中提的一个问题我印象很深刻,是说为什么我的平台执行不了 Jmeter 自带函数?
为此我打了几十个断点来追查问题,最终确认了是动态的系统变量少了东西。
印象深刻一是因为源码查的真的很深入,看过源码的会了解,Jmeter 对 jmx 树的解析相当复杂,能有几十个各种实现类,断点很不好打。二是我真的一度放弃过,当然最终坚持下来并很快解决了。

然后可惜,代码还是来到了 8000 行,我的 “小而美” 去哪里了。

为什么执着于 Jmeter-API

就一压测任务,你直接调用 Jmeter 脚本执行就好了啊,也有测试报告查看,也支持分布式压测啊。
原因其实不复杂:Jmeter-API 最开始支持那就一直维护下去了,这种方式支持的功能多很多,同时 grafana 的监控不太理想。

而监控的数据只能来自 Jmeter-API。

平台能带来什么

任何想通过 Jmeter-API 来调用 jmx 脚本的项目,其实都可以借鉴一下我的代码。
确实 Jmeter 的源码已经够可以了,但是当前 Jmeter-API 还是不太方便。

压测引擎

从我上面的功能代码介绍也能看到,最核心最费劲的就是压测引擎了,同时目前这部分实现算比较稳定的。
所以我打算先从最核心的开始。

我会先介绍整体,然后通过介绍各个重点需求的实现方式来逐步讲解代码。

前端入口

$.ajax({
    type: "POST",
    url: baseURL + "test/stressFile/runOnce",
    contentType: "application/json",
    data: JSON.stringify(numberToArray(fileIds)),
    success: function (r) {
        if (r.code == 0) {
            vm.reload();
        }
        alert(r.msg, function () {
        });
    }
});

Controller

@SysLog("立即执行性能测试用例脚本文件")
@RequestMapping("/runOnce")
@RequiresPermissions("test:stress:runOnce")
public R run(@RequestBody Long[] fileIds) {
    return R.ok(stressTestFileService.run(fileIds));
}

必要的 Jmeter 配置准备

本来想精简一下的,其实这部分 Jmeter 源码中写的比较复杂,因为 Jmeter 的功能更多,这些都是自己抽出来是最简单可用的。
解释一下,代码简单就是说读取了几个配置文件,jmeter.properties,user.properties,system.properties,将其中的配置项汇总一下。
设置一下的本地的 Locale 环境。
其实到这里,是可以仅将这 3 个配置文件抽离出来,即不需要整个 Jmeter 的 home 目录,仅要这 3 个配置文件就能运行 Jmeter 脚本。
甚至仅在代码中写要的配置,都不需要实体的配置文件即可。

当然随着功能越来越多,平台跟 Jmeter 的耦合也越来越多,这个 Jmeter_home 目录还是越来越必要了。
主要是为了异步的生成测试报告,Jmeter 自带函数的一些必要的加载,
当然要完全去掉 Jmeter_home 目录的耦合也完全可行,但这会无形之中提高不少维护成本,不太合适。

String jmeterHomeBin = getJmeterHomeBin();
JMeterUtils.loadJMeterProperties(jmeterHomeBin + File.separator + "jmeter.properties");
JMeterUtils.setJMeterHome(getJmeterHome());
JMeterUtils.initLocale();

Properties jmeterProps = JMeterUtils.getJMeterProperties();

// Add local JMeter properties, if the file is found
String userProp = JMeterUtils.getPropDefault("user.properties", ""); //$NON-NLS-1$
if (userProp.length() > 0) { //$NON-NLS-1$
    File file = JMeterUtils.findFile(userProp);
    if (file.canRead()) {
        try (FileInputStream fis = new FileInputStream(file)) {
            Properties tmp = new Properties();
            tmp.load(fis);
            jmeterProps.putAll(tmp);
        } catch (IOException e) {
        }
    }
}

// Add local system properties, if the file is found
String sysProp = JMeterUtils.getPropDefault("system.properties", ""); //$NON-NLS-1$
if (sysProp.length() > 0) {
    File file = JMeterUtils.findFile(sysProp);
    if (file.canRead()) {
        try (FileInputStream fis = new FileInputStream(file)) {
            System.getProperties().load(fis);
        } catch (IOException e) {
        }
    }
}

jmeterProps.put("jmeter.version", JMeterUtils.getJMeterVersion());

对 Jmeter 脚本的必要加载操作

FileServer.getFileServer().setBaseForScript(jmxFile);

设置 jmx 脚本文件的工作目录,主要是可以根据这个来找到参数化文件及实现其文件流。

HashTree jmxTree = SaveService.loadTree(jmxFile);

加载 jmx 脚本,本身这个操作非常复杂。
jmx 脚本中通常会包含参数化文件,用户自定义的参数化,Jmeter 自定义函数,各种 Sampler 的实现,断言,甚至用户自定义的插件等等。
同时还有各种监听接口的初始化。
这些都是要找到实现类加载的,源码中包含非常多的实现类。

JMeter.convertSubTree(jmxTree);

去掉没用的节点元素,替换掉可以替换的控制器。
这个是递归实现的,本身这个方法我也没动过,看着就复杂。

单机执行脚本

JMeterEngine engine = new StandardJMeterEngine();
engine.configure(jmxTree);
engine.runTest();

初始化默认的压测引擎,分布式的压测引擎其实也是使用的这个。
configure 方法是设置回调的监听器,并添加状态。
runTest 就太复杂了,简单说几点吧:

  1. 设置各种监听器和配置准备,比如活动的用户数量的统计就会来自这里。
  2. 启动是按照 threadgroup 来的,按组启动的,脚本内众多相似的线程同属一个组。
  3. 设置的启动间隔时间,比如 1 秒内启动 100 个,这种的处理。
  4. 启动之前还要有一个 GC。(这对多脚本的同时进行有影响呀)
  5. 开始和停止之后都要通知各个监听器。
  6. 单个 thread 执行时遇到的断言判断,执行间隔,函数实现等。

其实这里我看的也不是很深入,复杂是一方面,代码其实注释也少,加的东西也太多了。
同时这里比较稳定(Jmeter 的核心不能不稳定)。

分布式执行脚本

java.util.StringTokenizer st = new java.util.StringTokenizer(slaveStr, ",");//$NON-NLS-1$
List<String> hosts = new LinkedList<>();
while (st.hasMoreElements()) {
    hosts.add((String) st.nextElement());
}
DistributedRunner distributedRunner = new DistributedRunner();
distributedRunner.setStdout(System.out); // NOSONAR
distributedRunner.setStdErr(System.err); // NOSONAR
distributedRunner.init(hosts, jmxTree);
engines.addAll(distributedRunner.getEngines());
distributedRunner.start();

StringTokenizer 是为了初始化 hosts 参数使用的,直接搬过来也没改。
DistributedRunner 本质上还是 StandardJMeterEngine 来执行的压测,使用的是 rmi 的协议实现的分布式压测。
执行不多说了,还增加了输出流和错误流。

最后

至此就会简单的执行 Jmeter 的 jmx 文件,当然我的代码中会包含各种其他的监听器,static 代码块,抛弃了 Jmeter 的 classloader,甚至还改了 Jmeter 的 Runner 代码。
这些主要是为了监听数据,加载 Jmeter 自带函数,可以同时进行多个 jmx 脚本,动态修改 Jmeter 配置文件等功能服务的。

  作为一位过来人也是希望大家少走一些弯路

在这里我给大家分享一些自动化测试前进之路的必须品,希望能对你带来帮助。

(软件测试相关资料,自动化测试相关资料,技术问题答疑等等)

相信能使你更好的进步!

点击下方小卡片

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于 Jmeter 的轻量级云压测平台的原理与实现 :压测引擎 的相关文章

  • 一文从0到1手把手教学UI自动化测试之数据驱动!

    在UI的自动化测试中 我们需要把测试使用到的数据分离到文件中 如果单纯的写在我们的测试模块里面 不是一个好的设计 所以不管是什么类型的自动化测试 都是需要把数据分离出来的 当然分离到具体的文件里面 文件的形式其实有很多的 这里主要说明JSO
  • 软件测试面试:还没有自动化测试项目经验,3个项目帮你走入软测职场!

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读2 3k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • 深入解析 YAML 配置文件:从语法到最佳实践

    一 认识YAML YAML YAML Ain t Markup Language 是一种人类可读的数据序列化语言 它的设计目标是使数据在不同编程语言之间交换和共享变得简单 YAML采用了一种简洁 直观的语法 以易于阅读和编写的方式表示数据结
  • Jmeter线程组中的RAMP UP

    我在 jmeter 中设置启动时遇到问题 我的测试场景如下所述 有3个线程组 Thread Group1 2 Users Ramp up 10 Thread Group2 3 users Ramp up 15 Thread Group3 5
  • JMeter 将变量设置为随机选项

    我一直在使用 JMeter 我知道 Random and RandomString功能 我需要选择一个随机选项并将其存储在变量中 因为它将用作多个调用的参数路径的一部分 例如 http www example com pets random
  • JMeter 中默认的响应超时是多少?

    如果我们没有在 HTTP 请求 采样器中设置任何超时 任何人都可以帮助了解 JMeter 中的默认响应时间吗 Thanks 它默认为0 无超时 设置超时的推荐方法是使用 GUI 如果由于某种原因它不适合您 您可以使用以下属性 用户属性 fi
  • Jmeter在执行过程中抛出“socketException:连接重置”错误

    我们要实现的目标是 500 个并发用户 我们尝试对 3 台机器上的 100 个用户进行测试 而且它运行得很好 没有任何错误 当我尝试使用相同数量的机器对 150 个或更多用户运行测试时 我开始收到以下响应代码 响应代码 非 Http 响应代
  • Jmeter 而控制器似乎没有将变量评估为数字

    我正在编写一个 jmeter 脚本 该脚本会不断加载数据 直到表达到指定的大小 我有一个 while 循环 其中有一个 HTTP 采样器来加载数据 然后是另一个带有 XPath 后处理器的 HTTP 采样器来检查表大小 它们调用两个不同的
  • JMeter在HTTP请求中使用beanshell变量

    我是这里的绝对菜鸟 我的意思是JAVA 花了几个小时寻找解决方案 现在我只想开枪自己 我想在 beanshell 断言中创建一个字符串 该字符串位于 HTTP 请求的正上方 在 beanshell 中我写道 String docid abc
  • jmeter无法记录浏览器操作

    我正在使用 apache jmeter 2 6 我想使用 HTTP 代理服务器记录浏览器操作 但动作并没有记录 我已经在线程组下定义了 HTTP 请求默认值 我为服务器名称指定了值 如下所示 http www xxxxx com 81 ht
  • Apache JMeter 的 Cookie 管理器未将 cookie 添加到 POST 请求

    我制定了非常简单的测试计划 登录 POST 返回会话cookie 获取状态 GET 返回用户状态 创建资源 POST 为资源提供 JSON 正文 所以我的 测试计划 如下所示 Test Plan Thread Group HTTP 请求默认
  • JMeter - 使用其他 BeanShell 预处理器/后处理器中的变量?

    有没有一种方法可以将变量从一个 BeanShell 预处理器 后处理器引用到另一个 BeanShell 处理器 它们在同一个线程组内 如果我在 HTTP 请求下的 BeanShell 预处理器内创建一个字符串变量 那么我可以在同一 HTTP
  • 暂停和恢复 jmeter 执行

    我正在寻找暂停和恢复 JMeter 执行 单 多线程 的选项 1 Pausing and Resuming through command line 2 From the Jmeter UI 3 By coding in some lang
  • 我有 JSR233 监听器,它在 JMeter 非 GUI 模式下似乎被忽略

    我在 HTTP 请求下有 JSR233 侦听器 它存储所有响应时间值 创建数组 然后对数组进行排序以查找 90 行 然后在达到最终 90 行阈值时标记最后一个事务 请求通过或失败 GUI 中的一切都运行完美 但我使用 Docker Imag
  • 在jmeter中设置整个请求url

    我有一个请求 它提供上传网址作为响应正文 uploadUrl https test com 9000 sample uploadurl I m able to extract the uploadUrl using JSON extract
  • Rest 服务器(Play Framework)在负载测试期间出现“Read Timed out”异常

    我们正在使用 Play Framework 在 REST 服务器上运行重负载测试 jmeter 350 个线程 35M 总请求 并在大约 2 小时后遇到以下错误 我们删除了其他组件 以便 request 只是接受请求而不执行任何操作 任何人
  • 与 Jenkins 一起在 JMeter 中响应断言 - 从不显示失败

    寻找一点指导 我有一个基本的 JMeter 测试计划设置来加载页面 我添加了几个断言来检查屏幕上的文本是否显示 对这些使用了响应断言 我使用 JMeter GUI 运行了该计划 并故意导致断言失败 错误率为 15 所以这很好 然后 我尝试使
  • 如何设置http请求的源IP?

    在发送 http 请求之前 我需要设置源 IP 地址 用于 IP 欺骗等 用于建立http连接的类是HTTPURLConnection 我在 stackoverflow 上找到了下面的链接 这非常有用 注册和使用自定义 java net U
  • JMeter:tearDown Thread Group的目的是什么

    我想了解JMeter中tearDown Thread Group的实际用法 在什么场景下可以使用tearDown Thread Group 根据提供的帮助JMeter 拆解线程组 http jmeter apache org userman
  • 手动设置Jmeter主目录

    根据这个帖子 http jmeter 512774 n5 nabble com Automatic generation of aggregate reports td4281459 html Jmeter home被自动检测到 有没有办法

随机推荐

  • ubuntu虚拟机搭建hadoop完全分布式集群

    一 需要的工具 需要的工具我已经完成分享 需要的可以直接在网盘中下载 VMware15 Workstation Pro 提取码 pp12 ubuntu16 18 19 镜像 提取码 yfj0 Xshell Xftp 提取码 6ao9 jdk
  • 7月网络学习报告

    原始代码 import torch import torch nn as nn import torch nn functional as F import torchvision from torchvision import datas
  • C++ 基础知识

    C 基础知识 一 语法 1 指针常量 常量指针 2 内存模型 3 引用 4 C 中面向对象的三大特性 5 纯虚函数和抽象类 6 文件操作 7 模板与泛型 8 STL 一 语法 1 指针常量 常量指针 指针常量 顾名思义 指针是一个常量 所以
  • 开源C/C++网络库比较 ace &&bosst &&libEvent

    ACE是一个大型的中间件产品 代码20万行左右 过于宏大 一堆的设计模式 架构了一层又一层 使用的时候 要根据情况 看你从那一层来进行使用 在开源的C C 网络库中 常用的就那么几个 在业界知名度最高的 应该是ACE了 不过是个重量级的大家
  • 盘点:计算机专业含金量最高的证书!只需一种证书在手,从此不愁

    第一种证书 计算机技术与软件专业资格考试证书 计算机技术与软件专业资格考试证书 是由国家人力资源和社会保障部 工业和信息化部领导的国家级考试 该考试分为 5 个专业类别 并分设了高 中 初级专业资格考试 共 28 个资格的考核 也是用人单位
  • 微服务设计指导-hystrix参数介绍

    连接nacos的配置 nacos 服务地址 spring cloud nacos discovery server addr nacos server addr 注册到nacos上的命名空间 spring cloud nacos disco
  • MySQL数据库基础知识11,查询缓存

    目录 一 查询缓存是什么 二 MySQL如何判断缓存命中 三 使用查询缓存需谨慎 四 如何分析和配置查询缓存 五 InnoDB和查询缓存 MySQL进阶实战系列文章 哪吒精品系列文章 一 查询缓存是什么 MySQL查询缓存保存查询返回的完整
  • 【OpenCV图像处理】1.23 直方图(Histogram)均衡化

    文章目录 相关理论 代码 展示效果 相关理论 什么是直方图 图像直方图 是指对整个图像像在灰度范围内的像素值 0 255 统计出现频率次数 据此生成的直方图 称为图像直方图 直方图 直方图反映了图像灰度的分布情况 是图像的统计学特征 直方图
  • 逆水寒紫禁之巅服务器维护,逆水寒"紫禁之巅"服务器或将成历史?王思聪、PDD时代已渐行渐远...

    原标题 逆水寒 紫禁之巅 服务器或将成历史 王思聪 PDD时代已渐行渐远 从没有一款端游 能像 逆水寒 这样 能吸引如此多的社会名流入驻游戏 在游戏开测之初 打着 五年五亿巨制 网易最后一款端游 的口号 连王思聪 秦奋等名人都纷纷入坑 逆水
  • 龙书笔记(9)

    chap 9 字体 生成和输出文本的3种方式 ID3DXFont CD3DFont D3DXCreateText 1 ID3DXFont接口 能处理一些复杂的字体和格式 但是速度略慢 创建ID3DXFont对象 D3DXFONT DESC
  • 爬虫:爬取Github项目结构、任意文件下载存储

    文章目录 场景描述 爬取 Github 项目的文件结构 爬取 Laravel 8 x 文件结构 编写脚本 访问 Github 连接超时 requests 读取时间超时 爬取脚本 任意文件下载脚本 场景描述 需求 发现 任意文件下载漏洞 后
  • C++ 结构体(struct)的继承

    C 中的struct对C中的struct进行了扩充 它已经不再只是一个包含不同数据类型的数据结构了 它已经获取了太多的功能 struct能包含成员函数吗 能 struct能继承吗 能 struct能实现多态吗 能 有很多人应该已经知道这样一
  • C++ 保留2位小数

    include
  • 索尼 toio™ 应用创意开发征文|小巧机器,大无限,探索奇妙世界

    文章目录 前言 微型机器人的未来 toio 小机器人简介 toio 小机器人 创新功能一览 toio 小机器人 多领域的变革者 toio 小机器人贪吃蛇游戏 代码实现 写在最后 前言 当我们谈到现代科技的创新时 往往会联想到复杂的机器和高级
  • VS2015新建项目时,左侧面板空白的解决方法

    VS2015新建项目时 左侧面板空白的解决方法 解决办法是 1 进入 C Users 当前用户名 一般为administrator AppData Local Microsoft VisualStudio 14 0 2 删除或重命名 Com
  • HDU 1007 Quoit Design竟然要分治

    Quoit Design Time Limit 10000 5000 MS Java Others Memory Limit 65536 32768 K Java Others Total Submission s 34742 Accept
  • Class 08 - 数据的读取和保存 & R语言中的管道(pip)功能

    Class 08 数据的读取和保存 R语言中的管道 pip 功能 数据的读取和保存 data 加载R中的数据集 readr 功能包介绍 readr 包中读取文件的函数 read csv 读取 csv 文件 readxl 包读取Excel文件
  • 小兔鲜儿 - 推荐模块

    目录 动态获取数据 静态结构 获取页面参数 获取数据 类型声明 热门推荐 渲染页面和Tab交互 热门推荐 分页加载 热门推荐 分页条件 type 和 interface 的区别 type 和 interface 的相似之处 type 的特点
  • C++ 静态库和动态库的区别

    库是C 中的函数集合 用于存放共享代码的 C 的库分为静态库和动态库 动态库将函数的声明和实现分开成两部分 分别存放在了两个文件中 而C 的函数声明就存放在了 lib 文件中 如果是静态库的话 lib 文件还会存放函数的代码本身和函数的实现
  • 基于 Jmeter 的轻量级云压测平台的原理与实现 :压测引擎

    目录 前言 平台的技术 平台的初衷 平台从开源开始到现在拥有了一些核心的功能 印象深刻的技术点 为什么执着于 Jmeter API 平台能带来什么 压测引擎 前端入口 Controller 必要的 Jmeter 配置准备 对 Jmeter