线程组
线程组作为JMeter测试计划的核心组件之一,对于模拟并发用户的行为至关重要。线程组元件是整个测试计划的入口,所有的取样器和控制器必须放置在线程组下。
可以将线程组视为一个虚拟用户池,其中每个线程可被理解为一个虚拟用户,多个虚拟用户同时执行相同的一批任务。
在这个虚拟用户池中,每个线程之间是相互隔离且互不影响的。每个线程的执行过程中,操作的变量不会对其他线程的变量值产生影响。
线程组的关键任务之一是定义并发用户的行为,包括设置线程数、循环次数、启动延迟等关键参数。通过适当配置线程组,测试人员可以模拟多用户在系统中同时执行任务的场景,从而评估系统的性能和稳定性。
通过灵活使用setup线程组、线程组、tearDown线程组、开放模型线程组,配置前置操作、主要操作、后置操作,更能真实、详细的评估系统。
线程组分为四类:
- 线程组
- setUp线程组
- tearDown线程组
- 开放模型线程组
线程组、setUp线程组、tearDown线程组控制面板中的元素基本一致:
- 名称、注释
- 在取样器错误后执行的动作
- 线程数
- Ramp-Up时间
- Same user on each iteration
- 延迟创建线程直到需要(只有线程组有)
- 调度器
开放模型线程组控制面板中的元素:
- 名称、注释
- 在取样器错误后执行的动作
- 调度计划
- 随机种子
取样器错误后执行的动作
在JMeter中,取样器(Sampler)是用于模拟用户请求发送到目标服务器的组件,例如HTTP请求、FTP请求等。当取样器执行过程中出现错误时,可以通过配置相应的动作来处理这些错误。以下是一些处理取样器错误时,线程组中常见方式:
-
停止线程
任何一个线程(用户)在执行过程中遇到错误时,该线程被停止,不影响其他线程(用户)。
-
启动下一进程循环
任何一个线程(用户)在执行过程中遇到错误时,Jmeter会立即停止当前线程的本次执行,并进行当前线程(用户)的下次执行,主要应用于线程多次循环时。 -
继续(无需演示)
JMeter将在取样器执行错误时,忽略错误继续执行本线程的后续操作及执行其他线程。
停止线程-多线程
示例接口代码
@ThreadGroup.route('/api/ThreadGroup5/', methods=['GET', 'POST'])
def threadgroup5(): return '200'
示例Jmeter脚本
-
测试计划下添加线程组
取样器错误后执行的动作
中勾选停止线程
线程数
:3
-
线程组下依次添加2个HTTP 请求取样器
名称:
错误请求-${yonghu}
(在前)、正确请求-${yonghu}
请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup5/
请求方式:
GET
-
线程组下添加CSV 数据文件设置(右键-添加-配置元件)
文件名
:ceshi.txt的路径
ceshi.txt文件内容:(复制后,手动删除前面的空格)
200,用户1
1111,用户2
200,用户3
文件编码
:UTF-8
变量名称
:ceshi,yonghu
-
错误请求取样器下添加响应断言
值:
${ceshi}
-
在测试计划中,添加查看结果树
运行结果
连续运行了3次,结果是一致的。总共有三个用户执行线程组,其中用户1
、用户3
完全执行成功;用户2
只执行了错误请求
。
因为设置取样器错误后执行的动作
为停止线程
,用户2
执行错误请求
时发生错误,Jmeter只会停止用户2
的后续执行,不会影响其他线程。
多线程组也是多线程,读者在实际的脚本编写中,要注意每个线程的情况去使用停止线程
。
停止线程-多循环
示例接口代码
@ThreadGroup.route('/api/ThreadGroup5/', methods=['GET', 'POST'])
def threadgroup5(): return '200'
示例Jmeter脚本
-
测试计划下添加线程组
取样器错误后执行的动作
中勾选停止线程
循环次数
:3
-
线程组下依次添加2个HTTP 请求取样器
名称:
错误请求-${xunhuan}
(在前)、正确请求-${xunhuan}
请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup5/
请求方式:
GET
-
线程组下添加CSV 数据文件设置(右键-添加-配置元件)
文件名
:ceshi.txt的路径
ceshi.txt文件内容:(复制后,手动删除前面的空格)
200,第1次循环
1111,第2次循环
200,第3次循环
文件编码
:UTF-8
变量名称
:ceshi,xunhuan
-
错误请求取样器下添加响应断言
值:
${ceshi}
-
在测试计划中,添加查看结果树
运行结果
连续运行了3次,结果是一致的。用户在第2次循环执行到错误请求
时,Jmeter停止测试。
因为设置取样器错误后执行的动作
为停止线程
,用户的第2次循环,执行错误请求
时发生错误,Jmeter停止用户的后续执行(就它一个线程)。
启动下一进程循环
示例接口代码
@ThreadGroup.route('/api/ThreadGroup5/', methods=['GET', 'POST'])
def threadgroup5(): return '200'
示例Jmeter脚本
-
测试计划下添加线程组
取样器错误后执行的动作
中勾选启动下一线程循环
循环次数
:3
-
线程组下依次添加2个HTTP 请求取样器
名称:
错误请求-${xunhuan}
(在前)、正确请求-${xunhuan}
请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup5/
请求方式:
GET
-
线程组下添加CSV 数据文件设置(右键-添加-配置元件)
文件名
:ceshi.txt的路径
ceshi.txt文件内容:(复制后,手动删除前面的空格)
200,第1次循环
1111,第2次循环
200,第3次循环
文件编码
:UTF-8
变量名称
:ceshi,xunhuan
-
错误请求取样器下添加响应断言
值:
${ceshi}
-
在测试计划中,添加查看结果树
运行结果
连续运行了3次,结果是一致的。用户执行了3次循环,其中第2次循环中,错误请求
出现错误,跳过正确请求
。
因为设置取样器错误后执行的动作
为启动下一线程循环
,用户的第2次循环,执行错误请求
时发生错误,Jmeter会跳过用户的本次执行,进行用户的后续执行。
ramp-up时间
ramp-up时间用于设置启动所有线程所需要的时间。例如:线程数设置为10,ramp-up时间设置为100秒,那么JMeter将使用100秒使10个用户启动并运行,即每个用户将在前一个用户启动后的10秒启动。
如果ramp-up值设置得很小、线程数又设置得很大,刚开始执行测试时会对服务器产生很大的压力。
示例接口代码
@ThreadGroup.route('/api/ThreadGroup5/', methods=['GET', 'POST'])
def threadgroup5(): return '200'
示例Jmeter脚本
-
测试计划下添加线程组
Ramp-Up时间
:9线程数
:3
-
线程组下添加1个HTTP 请求取样器
请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup5/
请求方式:
GET
-
在测试计划中,添加查看结果树
运行结果
连续运行了3次,结果是一致的。3
个用户执行线程组
,各个用户的请求时间分别为2024-04-15 16:12:37 CST
、2024-04-15 16:12:40 CST
、2024-04-15 16:12:43 CST
。
3
个用户执行请求的间隔时间正好是3
秒,即ramp-up时间/线程数
。
same user on each iteration(在每次迭代中使用相同的用户)
没有研究出来它有什么用。经过我的测试,same user on each iteration(在每次迭代中使用相同的用户)
启用与否,作用是一样的。
如读者对此有不同见解,欢迎与我联系,共同探讨。目前,我十分费解。
延迟创建线程直到需要
当在JMeter中启用延迟创建线程直到需要
时,JMeter会根据预设的Ramp-up时间
动态地分配线程。假设Ramp-up时间
设置为20秒,线程数
为10,那么JMeter会在测试启动后立即创建第一个线程并开始请求处理。随后,每隔2秒,JMeter将创建下一个线程,直到所有线程都被启动。
如果关闭“延迟创建线程直到需要”选项,JMeter会在测试开始时一次性创建所有线程。使用同样的参数,即在测试一开始,JMeter会立即创建全部的10个线程。这些线程会按照设定的“Ramp-up时间”进行执行,每个线程将间隔2秒启动。
延迟创建线程直到需要
这一配置的目的是为了应对测试机性能有限的情况。通过这种方式,可以避免在测试初期就创建所有线程,导致资源过度占用和可能的性能问题。这种方法有助于平滑地增加系统负载,同时防止资源瞬间紧张导致测试无法正常进行。
调度器-启动延迟
调度器
主要控制线程操作时间。启用调度器
后,可以输入持续时间
(值不能为空),启动延迟
来控制线程组的操作时间及线程组操作前的延迟时间。
同时输入持续时间
,启动延迟
时,先计算启动延迟
,再计算持续时间
。
示例接口代码
@ThreadGroup.route('/api/ThreadGroup5/', methods=['GET', 'POST'])
def threadgroup5(): return '200'
示例Jmeter脚本
-
测试计划下添加线程组
启用
调度器
持续时间
:10启动延迟
:3
-
线程组下添加1个HTTP 请求取样器
请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup5/
请求方式:
GET
-
在测试计划中,添加查看结果树
运行结果
注意看图中右上角-黄色三角形左边的计时器
,值固定在2
秒。这个计时器计算整个测试计划的持续时间。由于示例请求的接口响应较快,可以理解为计时器的时间就是HTTP请求
时的时间。
因为启动延迟
设置为3
秒,所以HTTP请求
会在延迟3
秒执行。不过计时器的时间是2
秒,误差1
秒。我多次试过把持续时间
、启动延迟
的时间拉长,误差还是1
秒。
调度器-持续时间
调度器
主要控制线程操作时间。启用调度器
后,可以输入持续时间
(值不能为空),启动延迟
来控制线程组的操作时间及线程组操作前的延迟时间。
同时输入持续时间
,启动延迟
时,先计算启动延迟
,再计算持续时间
。
示例接口代码
@ThreadGroup.route('/api/ThreadGroup6/', methods=['GET', 'POST'])
def threadgroup6(): sleep(3) return '200'
示例Jmeter脚本
-
测试计划下添加线程组
启用
调度器
持续时间
:2 -
线程组下添加1个HTTP 请求取样器
请求地址:
HTTP://127.0.0.1:5000/api/ThreadGroup6/
请求方式:
GET
-
HTTP 请求下添加1个固定定时器
值:
3000
-
在测试计划中,添加查看结果树
运行结果
图中结果树中什么都没有,这是因为线程组的持续时间
只有2
秒,但固定定时器
的延迟有3
秒,导致还未执行取样器
,持续时间已经结束。
此时删掉固定定时器,运行结果
此时有人会有疑问。接口中设置的休眠时间
就已经是3
秒了,脚本中的持续时间
还只是2
秒,为什么这次成功执行了呢?
持续时间
的设置,只作用于还未执行的取样器。已经执行的取样器,无论等待多长时间,都会执行完成。