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

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

希望大家指出我的不足之处。
希望能帮我点上一颗 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 条回复 时间 点赞

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

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

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

对 槽神 回复

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

对 zyanycall 回复

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

对 徐汪成 回复

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

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

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

对 zyanycall 回复

嗯 我研究研究。

对 zyanycall 回复

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

对 徐汪成 回复

好的,👍

仅楼主可见
对 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 ) 了解一下. 重复的轮子

对 老马 回复

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

我觉得重要的是思想。

对 徐汪成 回复

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

对 老马 回复

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

16楼 已删除
仅楼主可见

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

对 cctodd 回复

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

对 徐汪成 回复

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

https://github.com/AsuraTeam/monitor

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

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

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

对 老马 回复

我以为没人看呢……

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

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

对 zyanycall 回复

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

pom 文件用哪个?

对 boe1900 回复

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

对 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 的路径,目录下是这样的:

对 zyanycall 回复

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

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

对 boe1900 回复

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

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

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

40楼 已删除

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

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

对 zyanycall 回复

大哥 这个怎么执行压测?

对 zhanglimin 回复

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

对 hhh123x 回复

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

对 魏海峰 回复

有执行压测的启动按钮。

对 zyanycall 回复

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

对 zyanycall 回复

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

对 魏海峰 回复

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

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

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

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

对 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 文件?

52楼 已删除
对 zyanycall 回复

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

对 魏海峰 回复

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

对 zyanycall 回复

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

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

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

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

对 魏海峰 回复

是吧,平台上传 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 脚本的自带功能:

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

84楼 已删除
仅楼主可见
zyanycall · #86 · 2018年11月26日 Author
仅楼主可见
87楼 已删除
仅楼主可见
对 老马 回复

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

对 老马 回复

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

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

对 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

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

98楼 已删除
仅楼主可见
对 zlba111 回复

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

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

你们还招 leader 吗,加个 wx 被

对 大奇 回复

喵喵喵?你想应聘我 leader?

你好这是报什么错

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]

对 chen 回复

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

对 chen 回复

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

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

对 xidianzxm 回复

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

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