性能测试工具 基于 Jmeter 的轻量级云压测平台的原理与实现 (二):压测引擎

zyanycall · December 13, 2018 · Last by 惜木 replied at March 06, 2020 · 7608 hits

希望能帮我点上一颗 star,多谢。
感谢大家对平台的使用和支持,希望大家多提 issue,多和我讨论。

github:[https://github.com/zyanycall/stressTestPlatform]

平台的技术

平台的初衷

平台的核心初衷很简单,就是能在浏览器中完成一系列 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 配置文件等功能服务的。

共收到 78 条回复 时间 点赞
蓝蓝 有咋开源的压测服务化平台项目吗? 中提及了此贴 12 Feb 09:32

想问一下楼主 在上传 jmx 后启动 提示 异常请联系管理员,可否看出是不是环境问题,提示:
Problem loading XML from:'D:\Wanglei\apache-jmeter\apache-jmeter-4.0\Performence\20190213215933351\case20190213215933768.jmx', missing class com.thoughtworks.xstream.converters.ConversionException:

回复

你本地的 JMeter 装了插件,服务器上没装,解析 jmx 报错

我刚开始也想到了,但是我只用到了普通线程组,http 请求取样器 和 header 管理 还有查看结果树,TPS,ResponseTime 图 而且编写脚本的 jmeter 和执行的 jmeter 都是本地的同一个文件 应该不会有插件问题

回复

arrow 回复

把。TPS 和 Responsetime ,查看结果树 删掉后 确实成功了。。

回复

查看结果树应该没问题,主要是另外两个监听属于插件,删掉就可以了。
平台自带监听,比 jp@gc 的监听要和谐一些(自认为功能也好了不少😁 )。

zyanycall 回复

哈哈 非常感谢,因为我司重要项目压测的时候 一般申请 8-10 台(因为不太懂所以只能多加压测机器。。)8c16g, 这样的机器运行一个 slave 太浪费了。所以单个 ip 不同端口 部署多个 slave 机器 在试用的时候发现 StressTestSlaveService 添加的时候 ip 不能重复,仔细查看发现 test_stress_slave 表设置了 IP 为索引。于是把索引删了。。。保存成功 但是因为今天下午刚本地部署成功,还没来得及看代码(虽然也看不懂。。)不知道删掉这个索引 会不会触发其他问题

回复

没影响。
负载机是高 CPU 消耗的应用,除非机器 CPU 核心特别多如 32 核及以上,可以使用多进程,像你的 8C16G 的单进程就可以了,是没必要搞单 IP 多端口的多个 slave 进程的。
当然你们可以多尝试尝试。

已 Star,期待楼主后续的分享(话说有没联系方式,加个微信聊一聊)
建议做成一个 docker 镜像吧,方便点

zyanycall 回复

关于 jmeter 也不是太懂。但是最多的时候一个 master 不做压力机,后面有 15 个左右的 slave,但是我来做一般每个 slave 最多 500 线程,然后看到网上有说每个 slave 不要超过 200 个线程,100 个最佳,所以也不太清楚到底多少合适,之前用的 8c16g 开 3 个进程 每个进程 300 线程左右,观察 slave 机器 CPU 只有在启动的时候会暴增,之后会平稳,内存使用也基本在 12g 以内。但是关于楼主说的负载机是搞 CPU 消耗的应用,这个在我这边用的有点奇怪,有时候做压测 CPU 使用会比较高,有的时间就比较低,不知道是为什么,😨
同时问一个问题,楼主的压测平台是避免了 负载机过多导致控制机性能不够用的场景么,但是看楼主分布式节点启动的还是 jmeter-server 问一下 master 机器是平台的系统配置的本地绝对路径的 Jmeter_HOME 么

感觉楼主对 jmeter 研究挺深的,希望以后有兴趣可以出一个单机 Jmeter 调优,和分布式 master&&slave 调优,比如内存堆栈大小,GC 值等

回复

高 CPU 消耗是说 slave 负载机的瓶颈一般是 CPU,或者网络带宽。
master 可以是 Jmeter_HOME 也可以是 服务器进程,两者可以选择,可以配置。
我的平台主要是让 Jmeter 的单机或者分布式更方便使用,目前并不会提高性能。

Jmeter 无论单机还是 slave 就是一个 Java 进程,按照调优 JVM 的方式调优就好了。

大佬,请问一下,如果我想部署到阿里云服务器的话,需要什么准备工作,看到你都是本地部署这样子的,放到云服务器还得指导一二啊。。。。还有,需要上传那个几十 mb 的 jmeter 包做依赖吗,谢谢~阿里嘎多~

zyanycall #16 · March 20, 2019 Author
测试初妹 回复

云服务器和本地服务器很类似吧。建议你本地搭完环境之后和你们运维合作,搭建阿里云服务器。
因为涉及服务器的开销价钱,用户权限,端口权限,磁盘文件大小等等。

zyanycall 回复

emmm,感谢那么神速的回复,其实我有一个学生版本的阿里云了,钱已经不是问题,啊哈啊哈~

学生党阿里云各位了解一下。

测试场景组装 是要实现什么功能

zyanycall #19 · April 08, 2019 Author
suda23 回复

这个功能已经废弃。后续都删掉。

目前在使用平台自身的定时任务时碰到了点问题,使用 Jmeter-Home 就正常了
如果完全使用 Jmeter-Home 来运行是否可行,
比如在生成报告的同时,是否还能继续下一个压测任务

之前好像看到书记您说过不用 jmeter5 的原因,但忘了在哪看到了,能再解释一下不升级到 jmeter5 的原因吗
每次拜读一、二的原文和评论都有些新的收获,再次感谢达康书记😁 希望还有第三篇

zyanycall #21 · April 15, 2019 Author
suda23 回复

你好,感谢对平台的肯定。
定时任务这块如果是要结合 web 平台来做,肯定是需要代码量的,这部分我还没开发。如果是基于 Jmeter 的,那么使用 Jmeter-Home 应该是可以的(这你也试过了可以,感谢),因为使用 Jmeter-Home 的原理就是执行 Jmx 脚本,和使用源生 Jmeter 是一样一样的。

放弃 Jmeter5 是因为我当初在测试的时候,发现 Jmeter 5 对参数化文件支持的不好,具体我也忘记了,我们就是普通的使用回车换行的参数化文件,但那天是遇到了问题一直报请求失败,当时还没有调试脚本的功能,追查了很久发现回退 Jmeter 版本就没问题了,所以就一直保留 Jmeter4 的 api 方式,一直不升级。

以上是对你问题的一些回答吧,下面我扯点儿别的。

  1. 定时任务为啥一直不做呢,因为我觉得定时任务总有一些弊端和不太实用的。 性能测试是一个严肃的事情,说每时每刻都需要人盯着都不为过,那么有了定时任务让其自动执行是不太安全的。可能你会说轻量级的相当于回归测试的压测需要定时执行,来检查系统的健康程度,我仍然认为这不是不盯着让它自动运行的理由,不够严肃,一旦出了问题得不偿失(比如各种脏数据问题内存的数据库的,给流量高峰添乱等等)。 说不太实用是因为除了定时任务,更需要的往往是钩子执行,如代码提交之后就进行性能测试,这样更有意义一些,就是说不仅仅时间是触发条件,其他操作也要成为性能测试的触发条件,结合起来才是最实用的。而这种开发量稍微大了些,稍微偏离了性能测试平台的主核心任务,这个需要集成更多的三方包来搞,或者说业内对这里已经有方案如 Jenkins+Jmeter 还是可以的。 同时 Jmeter 自己也有对定时任务的实现,简单的定时任务完全可以胜任,所以平台对这部分需求也不急迫。
  2. Jmeter 4 已经满足了我现在的所有需求,所以我是觉得没必要赶潮流硬上 Jmeter 5,所以我也不升级。未来如果有场景非 Jmeter 5 不可,那我肯定会升级的。
zyanycall 回复

不知后续还有什么开发计划

我这边做了 2 个小改动,
1 是对于线程组的在线修改,压测过程中,调整并发还挺频繁的,所以做了一个这个功能

2 是对于参数文件支持指定 salve 进行同步,因为并发过程中多个 savle 用同一批数据的话,可能会出错

能搞成 docker 来部署就完美了。。。。

@suda23 同步 master 到 slave 上面会对文件自动分割么,假如参数化的文件是一次性文件,每个 slave 上面的同一个文件内容肯定是不同的。不然会报错。

另外想问一下楼主更新成分布式的时候支持不同 slave 可以执行不同的线程数,可以设定一个单台 slave 最高启动线程数量,当总线程数大于单台的时候,就自动拉起另一个试压机,直到达到指定线程数量,压测调试的时候不是每次都要开启所有的压测机器,但是不同的系统又占用相同的压测资源。前一段时间我这边有 1 台 16c32g+20 台 4c8g 压测机。我的办法是通过 Jenkins 执行,构建器为 1 新的压测任务让他自动排队,但是这样也会有资源浪费

zyanycall #26 · May 06, 2019 Author
回复

目前没做这么细。
每个 slave 执行的是相同的脚本,像线程数,步长等等都是一样的,本身 Jmeter 并没有 master 配置一个总的线程数,其他 slave 去平分这些线程数的概念(或者权重平分),都是 master 打算要执行一个脚本,其他 slave 都是按照这个来执行,这是两种压力设计,很明显你说的这种 Jmeter 本身就没有。
你说的这种其实要考虑的很多,比如一个脚本内配置了多个线程组,每个线程组的线程数都不一样,那你说的 “直到达到指定线程数量” 要做的就比较多了,比如要保持住每个线程组线程数的比例,要同步增加,或者说 “指定线程数量” 要细分化,即对每一个线程数都要有一个指定的线程数量,这样如果几个线程组还行,如果千百个线程组,你这要如何细分。这就是举个例子吧,意思是说实际要做的没有那么简单。
Jmeter 这种压力设计是有它的考虑的,我也认为是最省事的。
平台目前不想颠覆 Jmeter 的理念,平台的核心目前还是保持住 Jmeter 的所有特性,是保持,不是另立门户。

明白,因为最近的项目客户请了 TestIn 的同行来做压测,看到他们封装的 Jmeter 平台支持在平台设置脚本线程数量,以及运行时间,保存请求失败结果等等,联想到之前多个压测机器用不到的时候会有资源浪费,他们的平台就会自动分配每个 slave 机器的线程数,至于 “直到达到指定线程数量” 楼主可能没理解,这个意思是假如每个压力机平台可设置最高启动 1000 线程,当我在平台设置总线程数量为 7500 的时候平台就可以算出需要 8 台压力机,具体是使用 8 台压力机平分 7500 这个数字(原生),还是控制 7 台压力机执行 7000,一台执行 500,平台可以在做研究那种方法比较好。同时楼主提到的多个线程组的控制,这个我没到没想到,因为 jmeter 整体是一个进程,每个线程组线程数的比例,jmeter 原生本身会做控制,这个并不需要平台介入吧。不过确实 TestIn 同事的脚本所有的场景都放在了一个线程组下面,用控制器来隔离,很大很大。不知道是不是他们的平台也碰到了楼主说的线程组线程分配的问题。

zyanycall #28 · May 06, 2019 Author
回复

“每个线程组线程数的比例,jmeter 原生本身会做控制,这个并不需要平台介入吧”,据我了解,Jmeter 本身控制不了比例,能控制的仅是数量。平台需要根据比例计算出数量,传给 Jmeter 让其执行。
这种解决办法也有吧,一个线程组一个脚本,多个脚本同时执行达到多个线程组同时进行的效果,这也是可以的,可以避开问题,曲线救国吧。

我这边因为资源有限,所以简单做了一个 jmeter 和 Jenkins 的集成,主要依靠 shell 脚本。
在 Jenkins 构建的时候设置脚本启动的线程数量,并通过变量传到构建的 shell 脚本里,shell 脚本是启动 jmeter 的命令通过全局变量把脚本运行时间和线程数量传输到脚本里面。把生成的日志&&html 报告等等指定在工程在 Jenkins 里对应的工作空间中,

可直接查看

zyanycall #30 · May 06, 2019 Author
回复

赞,做的还是不错的。

所以我就是每个场景放一个线程组,不然没办法控制线程比例,但是我认为这样已经可以了,在深就是 jmeter 的底层原理了,不太明白什么时候需要控制每个线程组的线程数量比例,原生只能控制线程组的数量,但是这样已经够了吧。针对每个场景不同的每个接口线程比例可能不同的话, 原生有很多控制器可以做到对单个线程组内线程进行比例控制吧,如果提升一级到控制不同线程组来达到不同场景的需求。原生应该做不到。原来的设计思想可能是每个线程组可满足一个场景即可,测试人员只需要控制线程组的线程数量,用控制器来分配线程即可。
楼主说的 “平台需要根据比例计算出数量,传给 Jmeter 让其执行” 我理解平台可以做到算出线程组的线程数量就 OK 了,简单看一下 jmeter 的启动日志会发现线程是根据线程组来启动的,线程的分配应该是按照脚本的执行顺序或者控制器来实施。平台介入的话应该会对原生的逻辑进行改动。

说的可能有点乱。但是总体意思就是平台只需做到可以控制线程组的线程启动数量即可。甚至平台用 总线程数/单个 slave 最大启动线程数量,结果舍去小数点并 +1,然后用总线程数/前面的结果即可。这样也不用更改 jmeter 原生的线程控制。
举个例子:
单个 slave 最高启动 1000 线程,需求 7500 并发
平台:7500/1000=7.5 舍去小数点并 +1=8,7500/8=938(四舍五入得整数)得出每个 slave 需启动 938 个线程即可,所有的 slave 线程数一致。无需更改原生的逻辑

Jenkins 挺方便的,同时利用 shell 脚本写了一些启动停止和重启 jmeter-sever 的脚本,利用 Jenkins 触发。

还有手工把文件分割成指定份数,并用脚本传不同文件到不同服务器并把名字改成一致,或者把相同的参数化的文件发送到 slave 机器上

感觉这些都可以在平台上面实现呢。如果可以做到,对我们这样的平民测试简直如有神助啊。

其实例如阿里的 PTS,腾讯的压测大师,美团的什么锤。大型的互联网公司都会有一些自己的封装一些压测平台的,最近在找比较方便的压测平台,感觉开源的就楼主的最好了。。
希望楼主可以持续更新。或者改成收费也行。

zyanycall 回复

楼主,感觉你的执行方法没有调对,我的服务器也装了插件,使用了 stepping thread groups 线程组,本地可以执行,用平台就报错,无法识别到这个类。

zyanycall #35 · May 31, 2019 Author
磨牙君 回复

插件自己加到环境里。

我把内核换成 5.1.1 生成报告的时候会报空指针

Author only

你好我问一下,脚本管理上传怎么不好用啊,后台没反应

zyanycall #39 · June 03, 2019 Author
kexinqw 回复

5.1.1 目前支持的不好,我都是用的 4.0 的。

zyanycall #40 · June 03, 2019 Author
磨牙君 回复

插件需要加载平台的 JVM 环境中。这个需要自己手动加载,如在 IDE 中引用三方 jar 包,pom 文件制定插件所在的目录等。

zyanycall #41 · June 03, 2019 Author
zmsky8888 回复

可以 debug 一下看看问题在哪里。详细的可以提一个 issue。

Author only


再次执行就报这个错

zyanycall #44 · June 03, 2019 Author
zmsky8888 回复

正常 JVM 环境中是有这个类的。你用的 Jmeter 是 5.1.1 吗。

Author only
zyanycall #46 · June 03, 2019 Author
zmsky8888 回复


是有这个类的。

Author only

楼主你这个可以支持集群发压吗

zyanycall #49 · July 02, 2019 Author
zmsky8888 回复

支持

Author only

没看到怎么集群发压呢?在前段页面启动,只调用一台机器

zyanycall #52 · July 27, 2019 Author
trygood 回复

集群的控制是 Jmeter 自带的,你如果看 Jmeter 源码就知道了。
平台只是调用 Jmeter 的 API,如果配置了集群就会使用集群。

嗯 奇怪的是 4.0 的 jmeter 没有 totaltps 这个类。。。

jmeter 的集群是这样的吗?脚本有 100 个线程,集群中有两个压力机,那么实际线上是 200?

zyanycall #55 · July 29, 2019 Author
trygood 回复

是的。
但是目前平台支持压力机压力负载配置,比如可以把两个压力机承担的都是原脚本压力的 50% ,这样加起来就是 100 了。
可以调大或者调小压力。
源生的 Jmeter 的分布式节点机的坑还是不少的,建议压测前重启。

zyanycall 回复

但是在报告中怎么看到的还是 100 线程呢?是 html 的报告有问题吗?
这个感觉不是很明确啊,比如:一个 master 调度两台 slave,脚本设置 100 线程,到底是 100 还是 200 线程呢

将 jmeter 集成平台最大的问题是依赖包要解决,LZ 选择拼装的模式,也是 OK 的,但不是最佳的

回复

完全这样实现,不仅仅是平分总线程数,还可以针对每个 agent 压力机进行比例分配和线程数分配。这种并不难,还有更深入的,压测执行过程可以动态增减线程数(细化到单台压力机)

分布式压测为什么不能生成调试报告?平台中分布式节点上执行不会自动停止,而且没有报告生成

分布式节点运行脚本报错

trygood 回复

建议你仔细了解一下 Jmeter 的自有的压力分布的实现。平台对 Jmeter 的压力分布实现原理配置等没有做任何修改,做的仅是优化。
节点机的压力负载调节也是在改动原 jmx 文件在内存中对象的方式来实现的。

ranran 回复

你这个报错是 Jmeter 自己报的,建议查一下配置。
之前我也说过,Jmeter 对分布式节点 slave 的管理很弱,这是源生的弊端。不过正常都会自动停止的。分布式停不下来建议直接重启节点没关系的。
调试报告仅仅是调试,我还没想分布式那么远,有点儿太复杂了。

怎么使用 standerpluning 提供的线程组?api 方式可以运行吗

trygood 回复

我没看懂你的问题。

如果想升级插件,或者趴其他第三方远的插件;放进项目如何操作

直接改源码吧,改 maven 引用。

有交流群吗,拉个群一起交流下可以吗

我在考虑另外一种做法,不用现有的 jmeter-server(没找到办法解决 master 和 agent 不在一个网络的情况),像 jenkins 一样,在每个 agent 上启动一个 server,负责 接收执行请求,下载脚本和数据,本地方式启动 jemter,上传 jtl 文件到 master,master 合并 jtl,生成报告,正在琢磨这样合并 jtl 会不会有问题

bessway 回复

这种思路很好,总的来说就是不要每次请求都要传数据,而是说 slave 先保存数据,测试结束之后走文件传输,例如 scp 把文件数据交给 master 整合。
其实这种方式我尝试过:

  1. Jmeter 自带 diskstore 存储模式,就会将请求结果保存在本地文件。但是这种方式不行,文件保存路径是默认的在/tmp 下,文件名是随机的如 SerialisedSampleSender383066585222874452.ser 并没有地方能指定这个文件是哪个脚本文件的,这个文件名的前缀后缀是 Jmeter 定义的,但是中间的数字是 JDK 的方法 File.createTempFile("SerialisedSampleSender", ".ser"); 定义的。生成的 ser 文件,格式内容是 xml 的,文件比较大,同时默认是如果进程退出则文件删除,要不然也不会在 tmp 里,同时文件有分割,大概 113M 左右,就会被分割成一个子文件。
  2. 如果是自己实现,slave 存储 jtl 或者 csv 文件,来传输给 master 再拼在一起,这种我试过,在某些字段如 99%,90% 这些,和 Jmeter 实时传输汇总的得到的测试报告不同,也就是说自己拼装的 和 Jmeter 源生的方式,得到的测试报告不一样,而 Jmeter 源生的测试报告业界更承认。 当然这种误差很小,不敏感的可以接受。

444104595@qq.com
qq:97891996

大佬 我发现了一个 bug,发邮件给你了。你查收下邮件啊 发到这个邮箱了 zyanycall@gmail.com

会飞的猪 回复

收到邮件了,问题我还没有复现(工作 ing)。
希望把问题提到 issue 上去,这样比较方便追踪(gmail 国内上稍微费劲)。
初步的直觉的判断,我觉得不太可能,因为纯粹是源生的 Jmeter 来驱动运行的,不太应该。但是你提到了吞吐率控制器,可能另有玄机。

zyanycall 回复

好的 我一会儿提到 issue 上 刚才提到的吞吐率控制器是这个 Constant Throughput Timer

最近用樓主開源的測試平臺,jmeter 更新到 5.2.1, 單機生成報告沒什麽問題

ryhnatwoods 回复

2020 年来了,都 5.2.1 版本了🤔

分布式执行时需要个问题。 调用 slave 机器时,如果模式
MASTER_JMETER_USE_SCRIPT_KEY 为 false,即在服务器进程内启动 Jmeter 压测 则会报下面的错误。但是选择 true,使用 jmeter 原生的,则无错误。 缺失的 rmi_keystore.jks 我是应该生成后放在哪儿呢?。目前 slave 机器已经把 ssl 关闭了

Set this if you don't want to use SSL for RMI

server.rmi.ssl.disable=true

报错信息
2020-01-23 14:34:42.396 DEBUG 23260 --- [nio-8080-exec-9] i.r.m.t.d.StressTestSlaveDao.queryList : ==> Parameters: 1(Integer)
2020-01-23 14:34:42.397 DEBUG 23260 --- [nio-8080-exec-9] i.r.m.t.d.StressTestSlaveDao.queryList : <== Total: 2
2020-01-23 14:34:42.397 INFO 23260 --- [nio-8080-exec-9] o.a.jmeter.engine.DistributedRunner : Configuring remote engine: 172.21.141.50:1099
Configuring remote engine: 172.21.141.50:1099
2020-01-23 14:34:42.401 ERROR 23260 --- [nio-8080-exec-9] o.a.jmeter.engine.DistributedRunner : Failed to create engine at 172.21.141.50:1099

java.rmi.ConnectIOException: Exception creating connection to: 172.21.141.50; nested exception is:
java.io.FileNotFoundException: rmi_keystore.jks (No such file or directory)
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:631)
at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:216)
at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:202)
at sun.rmi.server.UnicastRef.newCall(UnicastRef.java:338)
at sun.rmi.registry.RegistryImpl_Stub.lookup(RegistryImpl_Stub.java:116)
at org.apache.jmeter.engine.ClientJMeterEngine.getEngine(ClientJMeterEngine.java:72)
at org.apache.jmeter.engine.ClientJMeterEngine.(ClientJMeterEngine.java:85)
at io.renren.modules.test.jmeter.runner.LocalDistributedRunner.createEngine(LocalDistributedRunner.java:284)
at io.renren.modules.test.jmeter.runner.LocalDistributedRunner.getClientEngine(LocalDistributedRunner.java:260)
at io.renren.modules.test.jmeter.runner.LocalDistributedRunner.init(LocalDistributedRunner.java:140)
at io.renren.modules.test.service.impl.StressTestFileServiceImpl.excuteJmeterRunLocal(StressTestFileServiceImpl.java:574)
at io.renren.modules.test.service.impl.StressTestFileServiceImpl.runSingle(StressTestFileServiceImpl.java

小山 回复

Jmeter 默认 master 和 slave 之间,是需要 SSL 通信的,即加密通信。但是这个可以关掉。
因为我们一般都是内网,不存在窃取请求内容等安全性问题,还有 SSL 加密解密的过程会带来性能损耗。
所以一般都是关闭的。具体关闭的方式,日志中已经提示:server.rmi.ssl.disable is set to 'true' 这个配置在 master 的 jmeter.properties 文件中,slave 中也有一个。 改成 true 即可。

zyanycall 回复

master 和 slave 都已经设置为 server.rmi.ssl.disable=true。 主要问题是
如果平台模式
MASTER_JMETER_USE_SCRIPT_KEY 为 false,即在服务器进程内启动 Jmeter 压测 则会报下面的错误。但是选择 true,平台使用 jmeter 原生的,则无错误。

看代码似乎 MASTER_JMETER_USE_SCRIPT_KEY 为 false 时,是自己实现的远端调用

想请教,我用来压测服务器上某个接口用的本地机器,对压测结果会有很大影响吗?目前,我用两台不同的本地机器,同样的 jmeter 配置参数,来压测服务器上的一个接口,发现压测结果差别很大,请问这样的话,岂不是压测结果和用来压测接口的本地机器配置有很大关系?

81Floor has deleted
需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up