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

zyanycall · 2018年08月15日 · 最后由 徐汪成 回复于 2020年05月21日 · 9524 次阅读
本帖已被设为精华帖!

希望大家指出我的不足之处。
希望能帮我点上一颗 star,多谢多谢。

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

背景

云压测平台在测试领域并不是陌生的名词,简单来说就是在网页/移动端执行压测操作,同时压力机是部署在云端。
如压力节点机在云南,那就是云南产生压力,在北京,那就是北京产生压力。
现阶段云压测平台挺多的,我了解到的就有收费的如阿里云的 PTS、XMeter,还有一些开源的,如 nGrinder、云集微店的 TITAN。

云压测平台要解决什么问题

  1. 压测任务和业务的关系:隶属层级。
  2. 压测任务和测试人员的关系:权限管理。
  3. 摒弃繁琐的手工操作,提高效率,完全线上操作。
  4. 实时查看结果:集成监控平台。
  5. 历史压测数据留存:在线测试报告。
  6. 统一压测工具,统一压测标准。

云压测平台为什么要自己实现

收费的就不提了。
开源的各种压测工具,总会面临各种问题:

  1. 脚本语言写的压测内核,创造压力的性能就不够。
  2. 冷门的压测软件,测试结果难以服众。
  3. 软件用的人少,容易出问题和出了问题不好解决。
  4. 热数据图形监控都不好。
  5. 系统较庞大,占用资源较多。
  6. 是否便于推广,真正减轻工作量。

同时,平台实现之后还有好处:

  1. 和其他测试工具/平台做集成对接。
  2. 和其他部门的工具/平台做对接。
  3. 全链路性能测试的起点,公司性能保障的开端。

实现语言及内核

开发语言

其实平台本身使用什么语言开发都可以,但是由于压力内核选择使用了 Jmeter,为了要调用 Jmeter 的 API,平台也选择使用 Java 开发。

Jmeter 的优缺点

优点不详述了,最重要的还是顶级项目开源,社区活跃,Java 语言性能好和跨平台。

说下缺点,我目前发现的有:

  1. 代码还是庞大冗余,但这一定程度上保证了软件稳定性。
  2. 至少测试报告的核心开发,水平一般(我有实锤,要反驳的别在这吵)。
  3. Jmeter 的 API 设计的一般,调用时较麻烦。
  4. 分布式压测架构存在中心节点瓶颈,总压力上不去。
  5. 用大文件生成测试报告,时间较长。

所以国内已经有余力的公司(阿里)是在改造 Jmeter,就是取其精华去其糟粕了。

Jmeter 压测启动的方式

  • 脚本执行

平台根据前端的操作,自动拼接出一行可执行的命令,然后在指定服务器上执行这段脚本。
相当于是手工敲的命令平台帮着拼接和回车执行了。
即便是前端来生成测试脚本,也可以先保存成 jmx 文件,再脚本执行。
特点是平台和 Jmeter_Home 完全分离,带来的:

  1. 平台代码可以不用 Java 写了,什么语言写都可以,仅仅是拼装命令。
  2. 毕竟是脚本执行,Jmeter 随意切换版本。
  3. 平台和 Jmeter 可以不部署在同一台服务器上,即不是相同的进程内了。
  4. Jmeter 挂掉不影响平台运行。
  • 程序内引用 Jmeter 的 API 来压测执行

平台代码直接调用 Jmeter 的 API。
相对脚本执行的实现:

  1. 平台代码需要使用 Java 来写,毕竟要引入 Jmeter 的 jar 包了。
  2. 同样支持各种版本的 Jmeter,但是不灵活。
  3. 同平台在一个进程内产生压力,Jmeter 如果挂了,平台也危险,因为可以线程产生压力。

对比脚本执行的好处:

  1. 脚本执行得到的返回仅仅是窗口返回数据,太单薄且不稳定。
  2. 可以定制 Jmeter 功能,比如自实现压测监控数据。
  • 补充
  1. Jmeter 成名已久,程序还是很稳定的,至少 master 主节点我没遇到因为压测导致的崩溃。同时基于 JDK 的线程启停都很顺畅。
  2. 网上之前有声音说 Jmeter 2 系列版本性能更优,我认为是有一定依据的,至少代码量没现在这么臃肿,不过我自测的情况是,4 版本和 2.13 版本的压测性能差不多。
  3. 由于 Jmeter 3 版本才开始支持测试报告生成,所以我默认使用 Jmeter 4 版本的 API。
  4. Jmeter 的 API 随着版本更新有变化。

我的平台两种方式都支持,当然推荐是引用 Jmeter 的 API 方式启动,可配置。

从需求看实现

核心需求

  • 网页/移动端可以启动压测和停止压测。

最最基本的要求了。

  • 压测数据可以在线实时监控。

如果没有在线实时监控,那和 Jmeter 自身的脚本压测甚至和 AB 等工具,就没啥区别了。

  • 可能生成测试报告,最好在线查看,最少也要导出查看。

Jmeter 3 开始支持测试报告,这也是选择 Jmeter 的原因之一。

  • 支持分布式压测

即云压测的基础。

  • 权限管理

权限管理是平台面向全公司/全网的基础。

  • 业务层级展示

压测需要和具体业务有关系,这个关系在平台上要可以设置。

  • 压测热数据实时监控要可控

图形监控的功能要非常丰富,比如放大缩小等。

  • 删除,下载等操作

删除是让系统文件空间可控。下载是移动办公的基础。

  • 分布式节点管理

分布式压测的衍生需求,有了分布式节点管理,能大大减轻手工操作。同时各种提示非常人性化。

抛弃的需求 1:在线生成测试脚本

这曾经是我比较纠结的地方。

  1. 测试脚本要适应各种场景,各种协议,至少 HTTP(S),TCP 协议要有。
  2. 各种协议就要有不同的输入页面。
  3. 需要前端输入压测指标数据,如步长,虚拟用户数等等。
  4. 断言的实现。
  5. 前端录制脚本。

还有很多很多。
阿里的 PTS 是让在平台上写脚本代码实现最核心的功能包括断言,然后页面录入压测指标数据。
这样做如果遇到复杂的性能测试要求,问题很大:

  1. 调试困难,不解释了,在线调试代码,尤其移动端,画面太美。
  2. 参数化,测试数据关联要怎么破,尤其测试的请求特别多的情况。
  3. 高昂的学习成本,这么复杂的脚本代码我还搞什么工具推广,工具面对的是小白怎么办。

所以我的选择很简单,上传 Jmeter 的脚本,同时上传参数化文件。
好处不说了,详细的后续会介绍。

抛弃的需求 2:在线监控服务器指标

简单来说,就是平台可以实时监控到服务器的网卡,CPU,内存,磁盘,甚至日志等等。
我放弃的原因:

  1. 自研不如直接用开源。
  2. 运维和阿里云都有监控,功能重复。
  3. 自研性价比不高。

其实这个话题很大,比如监控到什么程度才算合格,能否做到智能化监控与测试报告的连接,服务器指标数据和服务器日志的结合展示分析等等。
在没想好怎么做之前,我的选择是抛弃这部分需求,做不好不如不做。

结尾

平台我一直在深度使用,挺好用。
平台代码当前也比较稳定。

平台创造压力的能力和 Jmeter 的内核相同,无论是单机还是分布式,同样的支持 Grafana+InfluxDB。

后续我会慢慢介绍这个平台,当然也可以直接看我的代码,我自认为代码注释,结构等,还是很清晰的。

压测引擎链接:

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

共收到 98 条回复 时间 点赞
魏海峰 回复

看来你这一下提了不少的需求啊,总体的数据结果分析 + 分布式节点的拆分利用。
简单聊聊吧。
数据结果 table 展示:
当然这个我还没做,因为 Jmeter 提供的测试报告是包含这些的。
你提的应该是抛弃测试报告的时候,这些聚合数据怎么弄?
我可以给折线图增加这些数据的线啊,Jmeter 传统的聚合报告的 table 展示是有弊端的,因为对比它,折线图不仅能展示趋势,还能包含历史数据,是更优的选择。
目前我没做因为我用不到 99%Line 等,还因为没人给我提这种需求。

分布式节点的拆分利用:
这个比较复杂了,其实 Jmeter 自己对这个支持的就不好啊,想一想,如果单独用 Jmeter,怎么利用多个 slave?还不是要多个 master。
我的平台想要展示不同的 slave 集群的结果,还要基于 Jmeter 的 API 之上,其实是有难度的。
这个地方我是有想法的,但是还没验证就不在这里说了。
我的原则是能不改 Jmeter 的源码就不改,因为它发展的很快,改它源码不合适。
是有想法的,嗯嗯,如果真有这方面的需求,是可以实现的。

魏海峰 回复

唉,也是一种取舍吧。
想要十全十美,肯定是要费一番功夫的。
其实性能测试到底目的是什么,这个要想清楚,后续我合计介绍介绍这个。
有些人都不看测试报告的,或者说报告中有什么根本看不明白,看不出来门道。
我们测试人想的,测试报告,什么趋势图,什么统计数据,其实对某些人来说,有了就是 “哦”,没有了就是 “你这不行呀”。
我们想的周到的,费力实现的,其实除了自己,别人都体会不到。
测试人不能自己把自己玩死,还是要聚焦到能提高自身价值的地方去。

魏海峰 回复

还是被说出来了啊……其实这个坑我也是深有体会的。文中我也提了。

这个坑,属于 Jmeter 的缺陷,但你的多 master 的方式也有问题,主要就是测试数据的监控收集和测试报告的整合及生成。
我想的解决方式,应该还是给 Jmeter 写插件,不要实时发送数据,而是固定间隔发送数据给 master。

当然目前我没心急火燎的解决这个问题,因为实际上,master-slave 的结构能产生的压力还凑合。
我这边是有数据的,master 是 1G 网卡的情况下,多 slave 能产生接近 8 万的 TPS 压力。
这个压力的数值,已经可以了。
通俗点儿说,这个压力我认为已经满足绝大多数的压力需求了。

这个问题我已经想着呢,会实现。

zyanycall 回复

嗯..用了...也拜读了你的代码... 确实很清晰易懂... 但是有个建议反馈给楼主:
jmeter master 和 slave 之间用的 RMI 协议进行通讯的,网络开销很大..在进行超大 TPS 压测时,master 的网卡时常会成为瓶颈.及时不成为瓶颈,经过在压测中数次验证, Master*1 Slave*3 架构可以发出的压力一般都是小于 Master *4 的... 在实战中,我都是使用多个 Master 的模式压测的... Master-Slave 的基本不用

用了大半年的 ngrinder,社区几乎死了一样,二次开发也多半集中在数据的展示,进一步较难。
现在在重新回到 jmeter,谢谢楼主分享的方案,有启发

我是做性能测试,说真的阿里云的 PTS 和腾讯的 WeTest 我都用了,但是功能是在太单调了,不能满足实际情况的需求。
所以我们都是自己写 Loadrunner 脚本和 Jmter 脚本,然后根据实际情况租用阿里云机器进行压测的,报告都是自己收集整理的,虽然确实繁琐,但是可以应对实际测试过程中的一些突发情况和奇奇怪怪的需求。

老马 回复

还不支持。
不过你这种方式真的是不错的,借助 docker 解决 client 部署的问题,甚至能解决 slave 的重复利用问题(需要测试验证不能贸然使用)。
目前平台还仅仅是支持 slave 的事先部署(也可以用 docker 部署 Jmeter),没有 docker 的这种脚本的自动批量部署和销毁(平台虽然没有但是使用者可以自己实现,然后将数据插入到数据库的 slave 表中即可)。

平台目前的职责非常单一清晰,就是把脚本能页面执行 + 页面监控 + 生成测试报告。脚本的页面执行包含了分布式情况下的执行。分布式环境的其实并不在平台的核心职责之内。

老马 回复

重复的轮子无所谓了。你说一个需求我能给你找 100 个实现的轮子。

我觉得重要的是思想。

徐汪成 回复

好的,👍

仅楼主可见
zyanycall 回复
### SQL: insert into test_stress_case_file         (         `case_id`,         `slave_id`,         `status`,         `origin_name`,         `file_name`,         `file_md5`,         `add_by`         )         values         (         ?,         ?,         ?,         ?,         ?,         ?,         ?         )
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'slave_id' cannot be null
; SQL []; Column 'slave_id' cannot be null; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'slave_id' cannot be null
    at org.springframework.jdbc.support.SQLExceptionSubclassTranslator.doTranslate(SQLExceptionSubclassTranslator.java:85)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
    at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73)
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
    at com.sun.proxy.$Proxy68.insert(Unknown Source)
zyanycall 回复

兄弟 上传不了脚本?

@zyanycall Taurus (Blazemeter reporting services ) 了解一下. 重复的轮子

老马 回复

外国人的轮子啊……不知道也是基于 Jmeter 的吗?
我文中写了不少为什么我要再开发一套的原因,要不你参考一下,然后告诉我 Taurus (Blazemeter reporting services ) 是不是都满足了。

仅楼主可见
cctodd 回复

楼主写的还是不错,代码也比较容易看懂,可以拿过去自己再进行二次开发。
另外,我觉得既然压测的核心已经交给了 jmeter,二次开发还是聚焦在数据展示上面比较好,如果能够实现 loadrunner 那种实时数据、监控资源展示,还有报表分析的能力那就牛逼了。

徐汪成 回复

多谢肯定。
实时监控的开源产品太多了,最近我还发现了一个 :

https://github.com/AsuraTeam/monitor

这方面要考虑的太多,自己实现很难专业。
另外提到的 报表分析 ,如果做出来确实很牛,就像 听云 ,但是到这步就很难开源了, 是可以赚钱的了。

正在用 go 写 jmeter 的性能测试平台 向楼主学习一下

@zyanycall 达康书记 首篇没了 第 二 第三篇呢

老马 回复

我以为没人看呢……

最近也在给团队开发这个高并发的性能测试平台,不过我是用 Python 手写的,并发可上万。可以交流一哈😁

如果能开源就好了 让我学习下。
不知道你底层的压测核心用的什么?

zyanycall 回复

持续关注中,期待后续原理和实现的分享 👏

zyanycall 回复

大佬,本地 windows 使用 IDEA 启动,生成报告会报 The JMETER_HOME environment variable is not defined correctly
This environment variable is needed to run this program

boe1900 回复

嗯嗯 , 需要配置一下 Jmeter_home 在数据库中配置。

zyanycall 回复


配置了,昨天在 mac 上跑成功了,win10 上不行,执行脚本的时候说环境变量未设置

boe1900 回复

我就是 win10 啊。
mac 成功了,win10 不成功,那先检查配置呗……主要是 Jmeter_home 的路径,目录下是这样的:

boe1900 回复

pom.xml 打 war 包使用 pom-war.xml

zyanycall 回复

可能是我的 apache-jmeter-4.0 有问题,我换成 apache-jmeter-5.0 的,然后把 pom 里的依赖换成 5.0 就好了

boe1900 回复

你这个问题 github 上也提了是吧,我在那里回答你。

兄弟,linux 上 mvn package,然后 java -jar renren-fast.jar,运行报错

基于 Jmeter 的轻量级云压测平台的原理与实现 (一):开篇 (改)
大牛,二和三呢?

您好 我用 jmeter 压测,请求数比较多,中间有出现 502 504 500 的错误,我怎么捕获这些呢。本来我想着用 python 写个结果分析文件,但结果太大了,jmeter3.0 能设置只要这些的结果吗

感谢使用。
第三方的 jar 包要写到 pom 文件里,相当于平台加载的三方 jar 包,然后重启平台使用。
这方面目前没有什么好办法。

请问下您这个性能平台支持 dubbo 接口的性能测试吗?

zhanglimin 回复

研究一下 Jmeter 的断言,能对响应码是 502 504 500 标记为错误或者成功。

hhh123x 回复

RPC 的接口吗?这个没尝试过。
我们平台是 Jmeter 内核的,如果 Jmeter 可以测,那平台就可以。

魏海峰 回复

有执行压测的启动按钮。

zyanycall 回复

替您回复下,Jmeter 是支持的...

zyanycall 回复

楼主,M-S 模式和多 M 模式各有优劣吧...我觉得 M-S 模式还有个问题就是较大 TPS,如你所说的 8W,跑稳定性测试的时候,比如跑 2 个小时,会产生巨大无比的 JTL 文件... 这个文件处理和分析都是噩梦... 而我的多 M 模式,则结果数据分散在多个 M 上,目前我是使用自己写的算法将 JTL--->JSON,将 JTL 里每 S 的数据聚合成 JSON 数组的一条,大概的数据结构就是: 2018102601<---> PASS:100,FAILED:10,AVG:10MS,TP99:15 ...,一个数十 G 的 JTL 产生的 JSON 文件一般都不超过 1MB,然后将各个 M 的 JSON 数据合并.
目前我的难点是实时的展示测试数据...

魏海峰 回复

没有呀,我的平台支持不生成 css 结果文件的,然后监控数据可以直接来我的平台截图呀,这样测试报告不会过于单薄,同时还会有监控。
你这个自己的算法还是不错的,但是还会面对一个问题,就是测试报告的整合。
你自己的算法也是改的 Jmeter 源码吗还是插件生成的?还是说处理已有的 JTL 文件?

zyanycall 回复

嗯. 不生成结果这个思路可以的... 有趋势图和统计之后的数据一般都足够了..
但是楼主这个好像没有那种汇总的数据... 就是 对整个测试做一个采样分析的比如 总平均 TPS,TP99,MAX,MIN 之类的...
另外就是一个最大的问题就是,很多公司都有诸如 dubbo、SAF 之类的 SOA 系统,测试这种类型的需求,需要:引入被测的 jar--->调用被测方法--->测试并统计结果...
楼主这种直接 CALL API 的方式,如果有多人同时使用并测试 SOA 的话,怎么搞呢?

zyanycall 回复

嗯. 内嵌调用 API 的方式 确实在 RPC 测试方面有天然的劣势... 这样会导致多个测试需求共用 JRE

  1. 可能因需要刷新 JRE 导致的不定时重启...
  2. 多个需求会频繁上传 jar,带来 jar 冲突之类的问题... 哎... 难...
魏海峰 回复

是吧,平台上传 jar 包这种操作,是测试自己掌握的。
不能说平台的管理还搞一堆其他部门的人来染指,那就说不过去了。
你说的包冲突,频繁上传,都是我们测试来掌握的。

测试的 jar 包要稳定,可以先 Jmeter 工具自身测试验证后,再到平台上来,所以你说的平台频繁启动刷新 JRE,是属于过度使用平台了。

zyanycall 回复

不是的... 使用 jmeter 测试 RPC 需求的时候,需要把测试 jar 和依赖的 jar 放到 JmeterHome\lib\ext 下面,可以预见经历若干个测试需求以后,JmeterHome\lib\ext 下会有大量重复的 jar... 引发 jar 包冲突...

不过如果想使用一套 jmeter 环境的话,可以考虑下面的解决方案;

  1. 自定义一个 jmeter.properties, 修改 search_paths 属性,指向某个测试任务专用的路径,可以和参数化文件放在一起;
  2. 启动 jmeter 时,使用-p 参数,用法:-p, --propfile the jmeter property file to use 这样可以防止测试 jar 污染 jmeter 环境
魏海峰 回复

嗯嗯。也可以。
我刚刚查了一下测试 dubbo 的。确实比较复杂。

zyanycall 回复

楼主,经我验证,你的平台目前需要改掉 3 个地方才能进行 RPC 脚本的测试;
StressTestFileServiceImpl.java 中修改三处:

// 1. 加入 loader 声明的全局变量

private static final DynamicClassLoader loader;

// 2. static 代码块的最后 加入
loader = AccessController.doPrivileged(
new java.security.PrivilegedAction() {
@Override
public DynamicClassLoader run() {
return new DynamicClassLoader(jars.toArray(new URL[jars.size()]));
}
}
);
// 3. setJmeterProperties() 方法开始加入
Thread.currentThread().setContextClassLoader(loader);

以上均经过实测.

魏海峰 回复

这几处代码是我当初没有放进去的。主要是因为最后一行:Thread.currentThread().setContextClassLoader(loader); 这会将当前线程的类加载器换成自定义的。
Jmeter 的自己的原有的启动类是需要这个的,为了将 lib 目录下的 jar 包的类加载到内存中,但我们平台是不同的,平台的环境里已经存在了绝大部分的类,没必要把 lib 目录下的所有的 jar 包都在每一个脚本执行的时候再加载一遍(应该还是占用内存的,即使重复也要来一套,没验证)。

如果你非要追求 Jmeter_Home 的那种全量的环境,可以使用脚本模式执行,同时外挂 influxDB+Grafana 监控,这是满足你的需求的。

当前平台还不打算搞的所有的 lib 包还要重复加载一遍。

zyanycall 回复

嗯嗯... 仅仅给是楼主提下建议... 主要是要测试 RPC 的需求的话,必须要将 RPC 接口本身的包引入 JMETER 环境...

恒温 将本帖设为了精华贴 11月04日 20:08

其实比如 PTS 也支持了原生 JMeter 的压测了,不用担心并发不够的问题,报表也是现成的。XMeter 不了解。

xmter 核心还是 jmeter 吧,

魏海峰 回复

有做类似的平台?能否分享?

请教一下,我脚本里使用了 BeanShell,Jmeter 脚本启动启动可以,但是以代码的方式,会报错,是有什么插件没有装吗?

boe1900 回复

你好,可以到项目的 issue 里去提一下问题。
需要看一下异常是什么才能确定到底缺什么包。

大佬,目前是以 shell 方式生成的测试报告,对 jmeter 不是很熟悉,请问可以 java 代码的形式生成吗?

boe1900 回复

可以的。
你可以查一下源码中的 org.apache.jmeter.report.dashboard.ReportGenerator 类, 是通过它生成报告的。
调用如下:
ReportGenerator generator = new ReportGenerator(reportFile, null);
generator.generate();

👍 关注中,期待二三,希望不会断掉

这个平台能否部署到 centos 系统呢?

jojotester 回复

可以的。

为啥生成的报告是 csv 格式的?

xulei 回复

你好,主要是因为我使用 jtl 文件生成测试报告会遇到异常,我看是 Jmeter4.0 里强制写的判断。

同时还有一些附带的原因:

  1. 相同的请求数量,jtl 文件比 CSV 的文件大。
  2. CSV 文件的内容比 jtl 文件的丰富,有一些指标是仅支持 CSV 文件的(jmeter.properties 文件中有说明,不详细贴了)。

可能我这方面了解的还不多,如果你有好的方式,让 Jmeter4 使用 jtl 来生成测试报告,一定要知道我一下如何做……对平台的结果展示很有用。

zyanycall 回复

//String reportPathDir = csvPath.substring(0, csvPath.lastIndexOf("."));
代码这句有问题,下载报告时报错,不应该只截取到点

xulei 回复

我本地执行是正常的。
建议把这种必现问题提到 issue 上吧,然后请详细说明一下报的异常是什么,问题现象是什么,多谢了。

因为我初步判断这里应该不存在兼容性问题。那么我的环境是 OK 的,想不到为什么你那里会失败。
提 issue 吧。

😂 在生成报告的时候,也遇到了 The JMETER_HOME environment variable is not defined correctly
跟楼上那位一样的问题,JMETER_HOME D:\software\apache-jmeter-4.0 没问题

jojotester 回复

😂 😂
建议直接使用 Jmeter 的脚本命令先试一下生成报告。
生成测试报告是建立在 Jmeter 本身的功能上的。
同时注意运行平台的用户和安装平台的用户权限是否一致。

这个异常信息是 Jmeter 自己打印的,我自己的异常信息都是中文大白话的……。

定时任务那块能讲解下吗?是如何定时执行测试用例吗?

jojotester 回复

定时的估计是用 spring-task 或者 quartz,设置定时规则,另起线程启动测试就行了。

jojotester 回复

目前的定时压测是使用本身 Jmeter 脚本的自带功能:

其他定时任务是执行平台的方法,需要个人对平台根据需求二次开发,和性能测试关系就不太大了。

仅楼主可见
zyanycall · #86 · 2018年11月26日 Author
仅楼主可见
仅楼主可见
老马 回复

这个人不是我,是拿我的东西给自己宣传了。
我 QQ 邮箱是 : 444104595@qq.com

zyanycall 回复

还有一点 就是我看你的 Report 是 Jmeter 自带的实际是性能方向的报告.
能支持接口测试方向的报告么? 比如 ant 那种的 或者 allure 等其他 report 框架那种的.

老马 回复

额,这是一个性能测试平台,不负责接口测试的事情。
接口测试的报告还是接口测试平台来做。

其实平台缺少的是一个调试脚本的手段方式,并且这个需求场景越来越频繁。

依次说一下吧。
ant 主要是卡在脚本执行结果文件格式上了。 ant 需要 jtl,但是测试报告需要 CSV,有冲突。
allure 等的是接口测试平台的职责,作为调试手段不如 ant 了。

MASTER_JMETER_USE_SCRIPT_KEY

TRUE or FALSE
FALSE 时,在服务器进程内启动 JMeter,是什么意思?

调试中遇到个问题,因为在测试计划中有个自用的 JAR 包,只能用本地 JMETER_HOME 这种方式

jojotester 回复

服务器进程内启动 JMeter,意思是使用 Jmeter 的 API 来启动脚本。

simple 专栏文章:[精华帖] 社区历年精华帖分类归总 中提及了此贴 12月13日 14:44
simple [精彩盘点] TesterHome 社区 2018 年 度精华帖 中提及了此贴 01月07日 12:08

学习了,感谢楼主分享!!我们公司也准备搭建这样的一个平台~~

仅楼主可见
zlba111 回复

说一下我怎么给 Jmeter 改动代码的吧:

  1. 发现 Jmeter 解决不了我的问题。比如单进程内同时间生成多个测试报告。
  2. 查报错日志位置,对源码进行 debug,看源码问题出现的位置。
  3. 分析问题,大概了解了问题所在,如测试报告无法并发是因为公共变量的覆盖。
  4. 尝试修改,是否 Jmeter 自身的 public 方法已经支持改动。这个例子是没有。
  5. 一般我是拷贝 Jmeter 的源码出来,放到自己的环境里,然后改动,这个例子就是在最后生成测试报告在字节流的时候,才覆盖公共变量,而不是一开始就覆盖掉。
  6. 调试,验证 OK。
Garfield 回复

你们还招 leader 吗,加个 wx 被

大奇 回复

喵喵喵?你想应聘我 leader?

赞一个,不过有一条不是很能同意:
高昂的学习成本,这么复杂的脚本代码我还搞什么工具推广,工具面对的是小白怎么办

性能测试要做好,所需要的技能可能比安全测试还要宽、深(可能有些维度上深度会差异较大),这点学习成本,就不用给做性能测试的工程师们省了
平台的好处就是海纳百川,搜集足够多的信息,也能够灵活快速支持分布式压测,这都是很赞的~另外,如果从测试配置和执行、监控结果中挖掘一些基础的分析和调优建议倒是个不错的思路

chen 回复

感觉,clean 一下,重新引用一下,就会没问题呢。

chen 回复

你应该用的 idea 吧? idea 跑会报这个错

xidianzxm 回复

百度 springboot java.lang.ClassNotFoundException: javax.servlet.ServletContext

Idea 启动报错,大佬帮忙看下
. ____ _ __ _ _
/\ / ' __ _ ()_ __ __ _ \ \ \ \
( ( )_
| '_ | '| | ' \/ ` | \ \ \ \
\/ _
)| |)| | | | | || (| | ) ) ) )
' |
__| .|| ||| |_, | / / / /
=========||==============|_/=////
:: Spring Boot :: (v1.5.6.RELEASE)

2020-05-21 09:38:04.816 INFO 56239 --- [ restartedMain] io.renren.RenrenApplication : Starting RenrenApplication on MacBookPro with PID 56239 (/Users/zhangxm/Downloads/Chrome_Download/stressTestPlatform-master/target/classes started by zhangxm in /Users/zhangxm/Downloads/Chrome_Download/stressTestPlatform-master)
2020-05-21 09:38:04.818 INFO 56239 --- [ restartedMain] io.renren.RenrenApplication : No active profile set, falling back to default profiles: default
2020-05-21 09:38:05.626 INFO 56239 --- [ restartedMain] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@359a57ad: startup date [Thu May 21 09:38:05 CST 2020]; root of context hierarchy
2020-05-21 09:38:07.500 WARN 56239 --- [ restartedMain] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [io.renren.RenrenApplication]; nested exception is java.lang.IllegalStateException: Failed to introspect annotated methods on class org.springframework.boot.web.support.SpringBootServletInitializer
2020-05-21 09:38:07.527 ERROR 56239 --- [ restartedMain] o.s.b.f.s.DefaultListableBeanFactory : Destroy method on bean with name 'org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory' threw an exception

java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.context.annotation.AnnotationConfigApplicationContext@359a57ad: startup date [Thu May 21 09:38:05 CST 2020]; root of context hierarchy
at org.springframework.context.support.AbstractApplicationContext.getApplicationEventMulticaster(AbstractApplicationContext.java:414) [spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.support.ApplicationListenerDetector.postProcessBeforeDestruction(ApplicationListenerDetector.java:97) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:253) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:578) [spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:554) [spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:961) [spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:523) [spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.destroySingletons(FactoryBeanRegistrySupport.java:230) [spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:968) [spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1030) [spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:556) [spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at io.renren.RenrenApplication.main(RenrenApplication.java:21) [classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_121]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_121]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-1.5.6.RELEASE.jar:1.5.6.RELEASE]

2020-05-21 09:38:07.542 ERROR 56239 --- [ restartedMain] o.s.boot.SpringApplication : Application startup failed

org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [io.renren.RenrenApplication]; nested exception is java.lang.IllegalStateException: Failed to introspect annotated methods on class org.springframework.boot.web.support.SpringBootServletInitializer
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:181) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:308) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:228) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:270) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:93) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:687) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:525) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at io.renren.RenrenApplication.main(RenrenApplication.java:21) [classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_121]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_121]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-1.5.6.RELEASE.jar:1.5.6.RELEASE]
Caused by: java.lang.IllegalStateException: Failed to introspect annotated methods on class org.springframework.boot.web.support.SpringBootServletInitializer
at org.springframework.core.type.StandardAnnotationMetadata.getAnnotatedMethods(StandardAnnotationMetadata.java:163) ~[spring-core-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.retrieveBeanMethodMetadata(ConfigurationClassParser.java:380) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:314) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:245) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:198) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:167) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
... 17 common frames omitted
Caused by: java.lang.NoClassDefFoundError: javax/servlet/ServletContext
at java.lang.Class.getDeclaredMethods0(Native Method) ~[na:1.8.0_121]
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701) ~[na:1.8.0_121]
at java.lang.Class.getDeclaredMethods(Class.java:1975) ~[na:1.8.0_121]
at org.springframework.core.type.StandardAnnotationMetadata.getAnnotatedMethods(StandardAnnotationMetadata.java:152) ~[spring-core-4.3.10.RELEASE.jar:4.3.10.RELEASE]
... 22 common frames omitted
Caused by: java.lang.ClassNotFoundException: javax.servlet.ServletContext
at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_121]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_121]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) ~[na:1.8.0_121]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_121]
... 26 common frames omitted

16楼 已删除

pom 文件用哪个?

40楼 已删除
zyanycall 回复

大哥 这个怎么执行压测?

52楼 已删除
84楼 已删除
87楼 已删除
98楼 已删除

你好这是报什么错

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'stressTestController': Unsatisfied dependency expressed through field 'stressTestFileService'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'stressTestFileService' defined in file [D:\IdeaProjects\stressTestPlatform\target\classes\io\renren\modules\test\service\impl\StressTestFileServiceImpl.class]: Instantiation of bean failed; nested exception is java.lang.ExceptionInInitializerError
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at io.renren.RenrenApplication.main(RenrenApplication.java:21) [classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_181]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_181]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-1.5.6.RELEASE.jar:1.5.6.RELEASE]

zyanycall 回复

嗯 我研究研究。

槽神 回复

😂 😂 也有道理!
不过平台的初衷之一是让开发等不专业的也可以执行性能测试,我们仅是管理测试脚本,管理权限就好。

zyanycall 回复

其实我比较想要 脚本、报告、数据等信息的管理维护,以及一个可以自动化关联测试的资源监控平台(可以应付绝大数的数据库、服务器、中间件等监控)。

徐汪成 回复

额,这种提到 issue 里吧……然后堆栈完整一点儿。
看错误是比较简单的,数据库字段不能为 null。我本地是可以的,当然完整堆栈能看出是什么问题。

徐汪成 回复

了解了,压测任务确实各有不同,不过只要是使用 Jmeter 的脚本,都可以上传到这个平台上进行压测。
至少可以做到:

  1. 脚本的统一保存。
  2. 生成 Jmeter 自带的测试报告,并在线查看。
  3. 通过复制镜像,生成完备的压力节点机,然后配置到平台上,一键启停。

如果你们的测试报告都是自己整理的,那么你们可能需要完备的监控截图,而我的平台监控截图比 grafana 的清晰了不少,你们可以作为参考。

zyanycall 回复

有时间准备拿你的来改造改造。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册