线程组
线程组作为JMeter测试计划的核心组件之一,对于模拟并发用户的行为至关重要。线程组元件是整个测试计划的入口,所有的取样器和控制器必须放置在线程组下。
可以将线程组视为一个虚拟用户池,其中每个线程可被理解为一个虚拟用户,多个虚拟用户同时执行相同的一批任务。
在这个虚拟用户池中,每个线程之间是相互隔离且互不影响的。每个线程的执行过程中,操作的变量不会对其他线程的变量值产生影响。
线程组的关键任务之一是定义并发用户的行为,包括设置线程数、循环次数、启动延迟等关键参数。通过适当配置线程组,测试人员可以模拟多用户在系统中同时执行任务的场景,从而评估系统的性能和稳定性。
通过灵活使用setup线程组、线程组、tearDown线程组、开放模型线程组,配置前置操作、主要操作、后置操作,更能真实、详细的评估系统。
线程组分为四类:
- 线程组
- setUp线程组
- tearDown线程组
- 开放模型线程组
线程组、setUp线程组、tearDown线程组控制面板中的元素基本一致:
- 名称、注释
- 在取样器错误后执行的动作
- 线程数
- Ramp-Up时间
- Same user on each iteration
- 延迟创建线程直到需要(只有线程组有)
- 调度器
开放模型线程组控制面板中的元素:
- 名称、注释
- 在取样器错误后执行的动作
- 调度计划
- 随机种子
执行顺序
在JMeter中,setUp线程组
拥有最高的优先级,而tearDown线程组
则具有最低的优先级。这一优先级关系可用表达式表示为:setUp线程组 > 线程组 = 开放模型线程组 > tearDown线程组
。
这意味着在测试计划执行过程中,setUp线程组
将首先执行,其后是线程组
和开放模型线程组
,最后执行tearDown线程组
。
通常情况下,setUp线程组
用于在测试执行之前进行一些初始化工作,而tearDown线程组
用于测试执行结束后的清理工作。
其他线程组在测试执行过程中按照它们在测试计划中的顺序执行。
示例接口代码
@ThreadGroup.route('/api/ThreadGroup/', methods=['GET', 'POST'])
def threadgroup1():
sleep(3) # 接口设置了休眠三秒,即接口在接收到请求数据三秒后,返回响应数据。
return "我是响应"
示例Jmeter脚本
-
在测试计划中,分别添加tearDown线程组、线程组、setUp线程组
-
在tearDown线程组、线程组、setUp线程组下分别添加HTTP请求取样器
名称分别为:
tearDown线程组
、线程组
、setUp线程组
请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup1/
请求方式:
GET
-
在测试计划下添加查看结果树
运行结果
可以观察到在2024-04-03 15:00:07
时,setUp线程组
中的取样器发送了请求。
随后,在 2024-04-03 15:00:10
时,线程组
中的请求取样器也发出了请求。
最后,在 2024-04-03 15:00:13
时,tearDown线程组请求取样器
发出了请求。
取样器
的请求时间间隔与接口设置的休眠时间一致,表明 JMeter 先执行setUp线程组
,随后执行线程组
,最后执行 tearDown线程组
。即使脚本中的顺序不是这样。
Jmeter中线程组的执行顺序可以归纳为:setUp线程组
> 线程组
= 开放模型线程组
> tearDown线程组
。
线程数
线程组中的线程数
指的是同时运行的虚拟用户数量。这个数量决定了你的测试将模拟多少个用户同时访问目标应用程序或服务。
在JMeter的线程组配置中,你可以根据你的需求来设置线程数
,通常是根据你想要模拟的用户负载量和你的系统资源来决定的。
示例接口代码
@ThreadGroup.route('/api/ThreadGroup2/', methods=['GET', 'POST'])
def threadgroup1():
return "我是响应"
示例Jmeter脚本
-
测试计划下,添加1个线程组
线程数:
3
-
在线程组下,添加HTTP请求取样器
请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup2/
请求方式:
GET
-
在测试计划中,添加查看结果树
运行结果
图中取样器结果中的Thread Name分别为线程组 1-1
、线程组 1-2
、线程组 1-3
。这是因为线程组
中线程数
值为3
,Jmeter启功了3个线程,每个线程皆执行1次线程组
。
在Jmeter中1个线程就代表1个用户,现在就相当于3个用户一起执行线程组
。
循环次数
在JMeter中,循环次数是指每个线程(即虚拟用户)执行其测试计划的次数。这个数量决定了你的测试将模拟用户访问目标应用程序或服务的次数。
在JMeter的线程组配置中,你可以根据你的需求来设置循环次数,通常是根据你想要模拟的用户访问上限值,如抽奖次数。
示例接口代码
@ThreadGroup.route('/api/ThreadGroup2/', methods=['GET', 'POST'])
def threadgroup1():
return "我是响应"
示例Jmeter脚本
-
测试计划下,添加1个线程组
循环次数:
3
-
在线程组下,添加HTTP请求取样器
请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup2/
请求方式:
GET
-
在测试计划中,添加查看结果树
运行结果
图中取样器结果中的Thread Name皆为线程组 1-1
。这是因为线程组
中循环次数
值为3
,Jmeter启功了1个线程,连续执行了3次线程组
。
在Jmeter中1个线程就代表1个用户,现在就相当于1个用户执行了3次线程组
。
取样器错误后执行的动作
在JMeter中,取样器(Sampler)是用于模拟用户请求发送到目标服务器的组件,例如HTTP请求、FTP请求等。当取样器执行过程中出现错误时,可以通过配置相应的动作来处理这些错误。以下是一些处理取样器错误时,线程组中常见方式:
-
停止测试
任何一个取样器在执行过程中遇到错误时,整个测试计划将会在所有当前正在执行的线程执行完毕后停止。
-
立即停止测试
任何一个取样器在执行过程中遇到错误时,JMeter会立即停止当前运行的测试,不再执行任何后续的请求或操作。
通过合理配置这些错误处理方式,可以根据测试需求和场景来控制测试计划的行为,确保对错误有适当的响应和处理。
停止测试-单线程组&单线程
示例接口代码
@ThreadGroup.route('/api/ThreadGroup3/', methods=['GET', 'POST'])
def threadgroup3():
if request.method == 'GET':
return '500' # 返回错误代码
return '200' # 返回正确代码
示例Jmeter脚本
-
测试计划下添加线程组
取样器错误后执行的动作
中勾选停止测试
-
线程组下依次添加2个HTTP 请求取样器
名称:
错误请求
(在前)、正确请求
(在后)请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup3/
错误请求
的请求方式:GET
正确请求
的请求方式:POST
-
错误请求取样器下添加响应断言
值:
1111
-
在测试计划中,添加查看结果树
运行结果
图中连续请求了三次,结果是一致的。
取样器在线程组中一般是从上至下顺序执行。错误请求
的结果报错后,因为设置取样器错误后执行的动作
为停止测试
,同时没有其他正在执行的线程,Jmeter直接结束本次测试。
删除响应断言,再次运行
图中连续请求了三次,结果是一致的。因为错误请求
的结果没有报错,会继续执行正确请求
。
停止测试-单线程组&多线程
示例接口代码
@ThreadGroup.route('/api/ThreadGroup3/', methods=['GET', 'POST'])
def threadgroup3():
if request.method == 'GET':
return '500' # 返回错误代码
return '200' # 返回正确代码
示例Jmeter脚本
-
测试计划下添加线程组
取样器错误后执行的动作
中勾选停止测试
线程数
:3
-
线程组下依次添加2个HTTP 请求取样器
名称:
错误请求-${yonghu}
(在前)、正确请求-${yonghu}
(在后)请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup3/
错误请求
的请求方式:GET
正确请求
的请求方式:POST
-
线程组下添加CSV 数据文件设置(右键-添加-配置元件)
文件名
:ceshi.txt的路径
ceshi.txt文件内容:
(复制后,手动删除前面的空格)
500,用户1
1111,用户2
500,用户3
文件编码
:UTF-8
变量名称
:ceshi,yonghu
-
错误请求取样器下添加响应断言
值:
${ceshi}$
-
在测试计划中,添加查看结果树
运行结果
图中连续请求了三次,结果是一致的。
可以看到每次执行都是执行完用户1
的线程后,因为设置取样器错误后执行的动作
为停止测试
,执行用户2-错误请求
时发生错误,同时没有其他正在执行的线程,Jmeter直接结束本次测试。
从这次测试中还可以看出,不仅线程组下的取样器有执行顺序,多线程也有执行顺序,即用户1>用户2>用户3
。
停止测试-单线程组&多循环
示例接口代码
@ThreadGroup.route('/api/ThreadGroup3/', methods=['GET', 'POST'])
def threadgroup3():
if request.method == 'GET':
return '500' # 返回错误代码
return '200' # 返回正确代码
示例Jmeter脚本
-
测试计划下添加线程组
取样器错误后执行的动作
中勾选停止测试
循环次数
:3
-
线程组下依次添加2个HTTP 请求取样器
名称:
错误请求-${xunhuan}
(在前)、正确请求-${xunhuan}
(在后)请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup3/
错误请求
的请求方式:GET
正确请求
的请求方式:POST
-
线程组下添加CSV 数据文件设置(右键-添加-配置元件)
文件名
:ceshi.txt的路径
ceshi.txt文件内容:
(复制后,手动删除前面的空格)
500,第1次循环
1111,第2次循环
500,第3次循环
文件编码
:UTF-8
变量名称
:ceshi,xunhuan
-
错误请求取样器下添加响应断言
值:
${ceshi}$
-
在测试计划中,添加查看结果树
运行结果
图中连续请求了三次,结果是一致的。
可以看到每次执行都是执行完第1次循环后,因为设置取样器错误后执行的动作
为停止测试
,执行第2循环时的错误请求-第2次循环
时发生错误,同时没有其他正在执行的线程,Jmeter直接结束本次测试。
停止测试-多线程组&勾选独立运行每个线程组
示例接口代码
@ThreadGroup.route('/api/ThreadGroup4/', methods=['GET', 'POST'])
def threadgroup4():
if request.method == 'GET':
return '500' # 返回错误代码
sleep(2) # 确保 与错误并行请求 在 错误请求 后结束
return '200' # 返回正确代码
示例Jmeter脚本
-
测试计划面板中勾选独立运行每个线程组
-
测试计划下添加2个线程组
名称:
线程组1
、线程组2
取样器错误后执行的动作
中勾选停止测试
-
线程组1下添加HTTP 请求取样器
名称:
错误请求
请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup4/
请求方式:
GET
-
线程组2下添加2个HTTP 请求取样器
名称:
与错误请求并行的请求
、错误请求之后的请求
请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup4/
请求方式:
POST
-
错误请求取样器下添加响应断言
值:
1111
-
错误请求之后的正确请求取样器下添加固定定时器(右键取样器-添加-定时器)
线程延迟:
3000
(确保 错误请求之后的正确请求 在 错误请求 后开始) -
在测试计划中,添加查看结果树
固定定时器
可以延迟取样器的执行时间,设置为3
秒,可以确保错误请求之后的请求
在错误请求
结束后执行。
运行结果
图中连续运行了多次。会发现,哎!怎么会有不一样的情况。我经过多次测试,发现在勾选独立运行每个线程组
&有多个线程组时,此时勾选停止测试
,并不会按照预期进行。
预期结果应该是,错误请求之后,Jemter直接结束测试计划,并不会执行线程组2。读者在使用时需要注意这种情况。具体原理咱们在这里就不深究了,欢迎任何有见解的读者联系我,我会贴在此段下面。
停止测试-多线程组&不勾选独立运行每个线程组
示例接口代码
@ThreadGroup.route('/api/ThreadGroup4/', methods=['GET', 'POST'])
def threadgroup4():
if request.method == 'GET':
return '500' # 返回错误代码
sleep(2) # 确保 与错误并行请求 在 错误请求 后结束
return '200' # 返回正确代码
示例Jmeter脚本
-
测试计划面板中不勾选独立运行每个线程组
-
测试计划下添加2个线程组
名称:
线程组1
、线程组2
取样器错误后执行的动作
中勾选停止测试
-
线程组1下添加HTTP 请求取样器
名称:
错误请求
请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup4/
请求方式:
GET
-
线程组2下添加2个HTTP 请求取样器
名称:
与错误请求并行的请求
、错误请求之后的请求
请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup4/
请求方式:
POST
-
错误请求取样器下添加响应断言
值:
1111
-
错误请求之后的正确请求取样器下添加固定定时器(右键取样器-添加-定时器)
线程延迟:
3000
(确保 错误请求之后的正确请求 在 错误请求 后开始) -
在测试计划中,添加查看结果树
运行结果
图中连续运行了多次。每次结果都一致,执行了错误请求
、与错误请求并行的请求
。
此时线程组1
和线程组2
是并发的,同时执行。所以在执行错误请求
时,也在执行与错误请求并行的请求
。另外错误请求之后的请求
因固定定时器的延迟,还未执行。
因为设置取样器错误后执行的动作
为停止测试
,虽然执行错误请求
时发生错误,但因此时还在执行与错误请求并行的请求
,Jemter会等待与错误请求并行的请求
执行结束后,在结束测试计划。
立即停止测试-多线程组&不勾选独立运行每个线程组
*
构想设计的立即停止测试-单线程组&单线程、立即停止测试-单线程组&多线程、立即停止测试-单线程组&多循环
;因不涉及多个取样器并行的情况,结果是与停止测试
是一致的。读者可以修改停止测试
为立即停止测试
进行尝试,此处不在赘述。
构想设计的立即停止测试-多线程组&勾选独立运行每个线程组
、与停止测试-多线程组&勾选独立运行每个线程组
的结果是一致的。读者可以修改停止测试
为立即停止测试
进行尝试,此处不在赘述。
示例接口代码
@ThreadGroup.route('/api/ThreadGroup4/', methods=['GET', 'POST'])
def threadgroup4():
if request.method == 'GET':
return '500' # 返回错误代码
sleep(2) # 确保 与错误并行请求 在 错误请求 后结束
return '200' # 返回正确代码
示例Jmeter脚本
-
测试计划面板中不勾选独立运行每个线程组
-
测试计划下添加2个线程组
名称:
线程组1
、线程组2
取样器错误后执行的动作
中勾选立即停止测试
-
线程组1下添加HTTP 请求取样器
名称:
错误请求
请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup4/
请求方式:
GET
-
线程组2下添加2个HTTP 请求取样器
名称:
与错误请求并行的请求
、错误请求之后的请求
请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup4/
请求方式:
POST
-
错误请求取样器下添加响应断言
值:
1111
-
错误请求之后的正确请求取样器下添加固定定时器(右键取样器-添加-定时器)
线程延迟:
3000
(确保 错误请求之后的正确请求 在 错误请求 后开始) -
在测试计划中,添加查看结果树
运行结果
图中连续请求了三次。结果是一致的,执行了错误请求
、与错误请求并行的请求
;但错误请求
是一个完整的过程,成功接受到响应数据;与错误请求并行的请求
则不然,在图中可以看到响应数据中有报错信息(Socket 关闭异常)。
这是因设置为立即停止测试
,在错误请求
发生错误时,Jmeter立即停止测试计划,不会等待与错误请求并行的请求
。
本文示例接口源代码可从前言中下载。