Jmeter 自带的线程组控件(Thread Group)中有三个重要的属性,分别是 Number of Threads, Ramp-Up Period, 和 Loop Count,用于控制线程组的行为。这三个属性的重要性与其文档的丰富程度严重不符。不仅变量名简略抽象,而且官方文档也含糊不清。本文尝试在实验验证的基础上,把这三个变量的含义解释清楚。
TL;DR
太长不看版:
用户请求模型
我们先来看简单的用户请求的模型。Jmeter 的每一个模拟用户,发起的时机和触发条件由 Jmeter 自己的调度策略指定。Thread Group 三个属性 No. of Threads, Ramp-Up Period, 和 Loop Count 默认都为1 。
三个 1 表示仅有一个用户,在整个测试过程中仅发起一次请求,且请求会在1秒钟内发出。如果把请求在时间轴上展开,将是像下图一样。
图中的蓝色方框表示测试脚本执行时间,Jmeter保证了请求在规定时间内发起。
在开始解释线程组属性之前,我们先看看如何验证对概念的理解是否正确。
验证方法
我们在 Jmeter 里用如下简单的测试方案能观察并且验证我们对概念的理解是否正确。
Test Plan
└─Thread Group
├─ jp@gc - Dummy Sampler
└─ jp@gc - Active Threads Over Time
将 Dummy Sampler 的响应时间默认值改为下图:
这样我们就能通过 Active Threads Over Time 插件观察 Jmeter 发起用户请求的调度逻辑。
比如上面的三个属性值都为1的情况,得到的记录是这样的:
图上右下角的红点的时间坐标是1秒,纵坐标是1,在整幅图里只出现了1次。这就是体现了三个属性值都为1的情况。
单个用户的 Loop Count
在分别讲解三个属性之前,希望您能忘掉先前对这三个属性的理解,因为错误的理解会妨碍您接收新的、正确的概念。
Loop Count 这个属性名称过于简略,无法直观的理解,容易引起误会。Loop Count 真正属性的含义是:
同一个用户,请求的重复次数
当取 Loop Count = 5
时,属性值列表如下:
Jmeter 发起请求的过程在时间轴上延伸是下图的样子:
通过上述的 验证方法 ,可以观察得到
再次发起请求的时机,大概是一次完整的请求结束后。即,响应结束后,发起下一次请求。
Ramp-Up Period
Ramp-Up Period,我认为这个名称可以翻译为"建立时间"。Ramp Up 的含义是指"从无到有"的时间,好比飞机从起飞到平飞的的过程。这里的含义指:
确保在此时间内,建立起全部线程
回头看上面 单个用户的 Loop Count 一节中,由于整个测试中只有一个线程,所以 Ramp-Up Period 这个属性其实是没有发挥作用的,因为任何测试初期至少要建立一个线程。只要建立了一个线程,Ramp-Up Period 就完成任务了。
所以,对于单用户的情况,无论 Ramp-Up Period 设置或长或短,都不会在 Active Threads Over Time 的图中体现出来。
比如,当单用户的 Loop Count = 10
时,属性列表如下时:
得到的 Active Threads Over Time 图形如下:
上图中,尽管 Ramp-Up Period 设置为1秒,但是由于在测试一开始就建立了全部的 1个线程,所以这个线程是可以持续工作到10秒钟的。和 Ramp-Up Period 设置为1秒没有关联。
但是,当多用户并存时,Ramp-Up Period 和 Loop Count 配合在一起,就能得到不同的线程调度结果,参见后面的 案例 。
用户数 Number of Threads(users)
Jmeter 使用线程模拟用户,增加一个线程就是多一个模拟用户。Number of Threads 表示模拟的并发用户数。
当取 Number of Threads = 5
时,由于上面 验证方法 一节中,Dummy Sampler 模拟的响应时间是 500ms
。为了便于观察结果,我们将 Ramp-Up Period 设置为 5秒
,这样能将5个线程发起请求的时间在时间轴上完全分开而不重叠。属性列表如下:
通过上述的 验证方法 ,可以观察得到
上图可以看出,Jmeter 在5秒钟内发起了5个线程,每个线程各发出1个请求。在时间轴上延伸是下图的样子,一条线表示一个用户:
下面我们尝试从几个案例来观察 Jmeter 在上述三个参数的不同组合下,如何发起请求。
案例1
属性值列表如下图:
上面讲过 ,单个用户时,Ramp-Up Period 不起作用。每个重复的请求将于前次请求结束后发起。因为每次响应时间是500ms,所以,上面属性组合的 Loop 将于30秒结束。得到的图是这样:
案例2
属性值列表如下图:
按上面讲的 ,Jmeter 将在60秒内启动60个线程,所以,Jmeter 的调度方法将是:
每一秒钟启动一个线程,并且在每个线程内发起一个请求。
整个过程将持续60秒,当每一个新的线程发起时,前次线程的响应已经结束,线程被销毁,所以每时每刻都只有一个线程存在。得到的图是这样:
案例3
属性值列表和 案例2 相同,改变的是 Dummy Sampler 的请求的响应时间,从 500ms
改为 1500ms
和 案例2 相同, Jmeter 的调度方法仍然是:
每一秒钟启动一个线程,并且在每个线程内发起一个请求。
但是,当每一个新的线程发起时,前次线程的响应尚未结束,所以每次观测时,总有两个线程同时存在,得到的图是这样:
(注意上图中的纵轴,几乎所有点都在 2 的位置)
案例4
先将 Dummy Sampler 的请求的响应时间,从 1500ms
改回 500ms
,再看线程组的属性值列表如下图:
按上面讲的 ,Jmeter 将在10秒内启动60个线程,所以,Jmeter 的调度方法将是:
每一秒钟内启动6个线程,并且在每个线程内发起一个请求。
由于每个请求的响应时间设置为 500ms,每一秒内启动的6个线程将有一半完成后被销毁,同时,整个过程将持续10秒,得到的图是这样:
案例5
线程组属性值列表如下图:
按上面讲的 ,Jmeter 将在60秒内启动10个线程,同时每个线程内重复6次请求,所以,Jmeter 的调度方法将是:
每6秒钟内启动1个线程,并且在每个线程内重复发起6次请求。
由于每个请求的响应时间设置为 500ms,所以每个线程内重复的6次请求将于3秒钟完成,每10秒钟之内,前3秒钟的观测结果,都有1个线程存活。得到的图是这样:
案例6
属性值列表和 案例5 相同,改变的是 Dummy Sampler 的请求的响应时间,从 500ms
改为 1500ms
Jmeter 的调度方法仍然是:
每6秒钟内启动1个线程,并且在每个线程内重复发起6次请求。
由于每个请求的响应时间设置为 1500ms,所以每个线程内重复的6次请求将于9秒钟完成,所以当 Jmeter 启动下一个线程时,仍然有前面线程中的最后2次请求没有收到响应,观测结果是这样:
案例7
属性值列表和 案例5 相同,改变的是 Dummy Sampler 的请求的响应时间,从 500ms
改为 1500ms
,线程组属性值列表如下图:
Jmeter 的调度方法改为:
每6秒钟内启动1个线程,并且在每个线程内重复发起60次请求。
由于每个请求的响应时间设置为 1500ms,所以每个线程内重复的60次请求将于90秒钟完成,所有未完成的线程将累积在一起。从第54秒钟最后一个线程建立起,到90秒的请求结束,整个过程应该是144秒,即2分24秒。观测结果证明了这一点:
案例8
Dummy Sampler 的请求的响应时间 500ms
,线程组属性值列表如下图:
Jmeter 的调度方法改为:
- 每1秒钟内启动6个线程,并且在每个线程内重复发起6次请求。*
每次启动的线程将和之前的线程叠加,观测结果证明了这一点:
案例9
Dummy Sampler 的请求的响应时间 500ms
,线程组属性值列表如下图:
Jmeter 的调度方法改为:
- 每6秒钟内启动1个线程,并且在每个线程内无休止的发起请求。*
因为请求永不结束,所以线程永不销毁,每次启动的线程将和之前的线程叠加,观测结果证明了这一点:
案例10
Dummy Sampler 的请求的响应时间改为 1500ms
,线程组属性值列表如下图:
和 案例9 相比,在更短时间内,创建更多的线程,线程的叠加更为明显,而且斜率增大。
Jmeter 的调度方法改为:
每1秒钟内启动6个线程,并且在每个线程内无休止的发起请求。
观测结果证明了这一点:
上图中,从第10秒钟开始,即保持 60 个线程不变。
结论
- Jmeter 将在 Ramp-Up Period 时间内,启动 Number of Threads 个线程。并且利用每个线程重复发出 Loop Count 次请求(或者说,Loop Count 次采样)
- 线程收到响应后,即发出 Loop 内的下一次请求
- 线程的请求 Loop 次数达到后,线程即销毁。
如果您对本文有疑问或者寻求合作,欢迎 联系邮箱 。邮箱已到剪贴板
精彩评论
本站 是个人网站,采用 署名协议 CC-BY-NC 授权。
欢迎转载,请保留原文链接 https://www.lfhacks.com/tech/jmeter-thread-properties/ ,且不得用于商业用途。