需求背景

最近项目新增了一个后端接口,功能大概就是传入区域的ID,响应该区域下所有的设备信息。

针对这个新增接口做性能测试,需要测试响应成功率、最大吞吐量等指标,涉及到HTTP请求的并行发送和结果信息收集。这次打算不用之前自己写python脚本的方式,改用JMeter试试看。

用JMeter实现上述测试需求的过程中,遇到了一些问题,自己摸索了一下解决方案,记录一下。

问题

使用JMeter自带的JSON提取器,从获取区域信息的响应中获取regionId的值后,被保存到了一个列表中,实际上是一组JMeterVariables。

我需要遍历这个列表,再用这个列表中的每一项作为请求体来并行发送请求。比如获取到了50个regionId,并行发送50个请求,每个请求的请求体包含一个regionId的值,类似:

for i in range(50):
    payload = {"regionId": region_id[i]}

一开始打算用JMeter的并行处理插件 parallel controller 和 forEach controller 来实现循环遍历和并行发送请求,但是发现无论是 parallel 在 forEach 控制器下,还是 forEach 在 parallel 下,都还是串行发送的请求。

猜测这个问题可能和插件实现机制有关,每个 forEach controller 都还是在一个线程中串行创建的。

不用 forEach,用 while 控制器 + 计数器来实现遍历,也是一样的结果。

解决方案

既然在同一个线程组里面没法用 parallel controller 来实现并行,那只能考虑用多个线程组来实现了,这就又涉及到了线程组之间的变量传递,我需要把上一个线程组中获取到的变量列表传递到下一个线程组中去。

折腾了一圈,最终用 BeanShell取样器+CSV文件存储的方式实现了。

  1. 添加一个 BeanShell取样器。在取样器中,编写脚本,把 regionId 的列表长度设置为全局变量,再把每个 regionId 都写入 csv 文件中去。这里不循环遍历把所有 regionId 都写到全局变量是因为研究了半天没弄出来__setProperty方法怎么加上变量索引。
FileWriter fstream = new FileWriter("F:/Documents/JMeter/result/src/regionId.csv",false);
BufferedWriter out = new BufferedWriter(fstream);

${__setProperty(regionNum,${regionIds_matchNr},)}

for(int i=1;i<=${regionIds_matchNr};i++) {
	String regionId_value = vars.get("regionIds_" + i);
//	log.info(regionId_value);
	out.write(regionId_value+"\n");
}
out.close();
fstream.close();
  1. 创建一个线程组,线程数设置为${__property(regionNum)},表示并行运行 regionId 数组长度个线程。Ramp-Up 时间可以根据测试需要自行设置合适值。循环次数设置为 1 ,并不需要重复运行。

  2. 在新创建的线程组中,添加 CSV数据文件设置,读取在上一个线程组中写入的 csv 文件,保存到变量中,比如 regionId,每个线程会自动把 csv 文件中当前线程数对应的行写入这个变量。

  3. 创建 HTTP 请求,请求参数设置为{"regionId": "${regionId}"}即可。

不要忘了在 Test Plan 中勾选“独立运行每个线程组”,否则所有线程组将一起启动。

可以看到,请求基本上都是并行发送的了。