线程组元件是任何一个测试计划的开始点。在一个测试计划中的所有元件都必须在某个线程下。所有的任务都是基于线程组。
Threads (Users)线程 用户
clipboard.png
上图可以看到,jmeter有三个添加线程组的选项,名字不一样, 但是创建之后,其界面是完全一样的。
setup thread group
一种特殊类型的ThreadGroup,用于在执行常规线程组之前执行一些必要的操作。在“setup thread group ”下提到的线程行为与普通线程组完全相同。不同的是执行顺序—它会在普通线程组执行之前被触发。
应用场景举例:
A、测试数据库操作功能时,用于执行打开数据库连接的操作。
B、测试用户购物功能时,用于执行用户的注册、登录等操作。
teardown thread group
一种特殊类型的ThreadGroup,用于在执行常规线程组完成后执行一些必要的操作。在“teardown thread group ”下提到的线程行为与普通线程组完全相同。不同的是执行顺序—它会在普通线程组执行之后被触发。
应用场景举例:
A、测试数据库操作功能时,用于执行关闭数据库连接的操作。
B、测试用户购物功能时,用于执行用户的退出等操作。
tips:
默认情况下,如果测试按预期完成,则TearDown线程组将不会运行。
如果你想要运行它,则需要从Test Plan界面中选中复选框“Run tearDown Thread Groups after shutdown of main threads”。
可能你还是不太理解他们与普通的线程组有什么不同。但是如果你用过junit,想必你应该对setup ,teardown这两个字眼不陌生。
如果你熟悉loadrunner,那你应该知道loadrunner的脚本中除了action里是真正的脚本核心内容,还有进行初始化操作的init脚本和测试完毕后对应的end脚本块。那么这里setup thread group 和 teardown thread group 就是分别指这两部分。
thread group(线程组)
这个就是我们通常使用的线程。通俗的讲,一个线程组可以看做一个虚拟用户组,线程组中的每个线程都可以理解为一个虚拟用户。多个用户同时去执行相同的一批次任务。每个线程之间都是隔离的,互不影响的。一个线程的执行过程中,操作的变量,不会影响其他线程的变量值。
线程组基本设置
取样器错误后要执行的动作:
继续:忽略错误,继续执行
Start Next Thread Loop: 忽略错误,线程当前循环终止,执行下一个循环。
停止线程:当前线程停止执行,不影响其他线程正常执行。
停止测试:整个测试会在所有当前正在执行的线程执行完毕后停止
Stop test now:整个测试会立即停止执行,当前正在执行的取样器可能会被中断。
这几个配置项控制了“当遇到错误的时候测试的执行策略”是否会继续执行。
设置线程数:
线程数也就是并发数,每个线程将会完全独立的运行测试计划,互不干扰。多个线程用于模仿对服务器的并发访问。
设置ramp-up period:
ramp-up period用于设置启动所有线程所需要的时间。
如果选择了10个线程,并且ramp-up period是100秒,那么JMeter将使用100秒使10个线程启动并运行。
每个线程将在前一个线程启动后10(100/10)秒后启动。
当这个值设置的很小、线程数又设置的很大时,在刚开始执行时会对服务器产生很大的负荷。
Ramp-up period的大小问题,对于初学者来说是最容易困扰的。
以下是 5 个线程依次从启动到执行退出的示意图:
红色框起来的部分才是真正 5 个线程并发请求的时间段。
假设我们设置 20 个线程,只运行 1 次迭代,看看不同的启动时间设置会有结果有何不同。
如果启动时间设置为 0,那么测试一开始就会产生 20 个并发请求,服务器万一只能承受 15 个并发,岂不是一上来就 gg 了,还测个什么呀。
如果启动时间设置为 40,那么会每隔 2 秒 启动 1 个线程。万一线程执行不到 1 秒就退出了,第 2 个线程 启动的时候,第 1 个线程已经退出了,不就是只产生了 1 个并发请求么。
那么设置成多少合适呢?我也不知道,但是结合我查阅的资料,可以给出一个参考意见。
第一步,把线程组跑 1 次(可以在线程组元件上右键选择 Validate),从聚合报告获取到吞吐量(Throughput)。
第二步,用线程数量除以吞吐量,得出启动时间。
例如,200 个线程,跑一次获取到吞吐量为 4/sec,启动时间为 200 / 4 = 50。这样设置以后,第 2 个线程启动后,刚好第 1 个线程执行完开始新的迭代,从而形成梯度递增的并发请求。
设置循环次数:
该项设置线程组在结束前每个线程循环的次数,如果次数设置为1,那么JMeter在停止前只执行测试计划一次。
如果执行很快的,则:
QPS = 线程池 / Ramp-up * 循环次数
所以这3个参数需要配合使用才能压到想要的qps。
Delay Thread creation until needed:
默认情况下,测试开始的时候,所有线程就被创建完了。如果勾选了此选项,那么线程只会在合适的需要用到的时候创建。
线程组调度器设置
调度器配置可以更灵活的控制线程组执行的时间
1、持续时间(秒):
控制测试执行的持续时间,以秒为单位。
2、启动延迟(秒):
控制测试在多久后启动执行,以秒为单位。
3、启动时间:
控制测试在某个时间点启动。但这个配置会被“启动延迟”配置覆盖。
4、结束时间:
控制测试执行的结束时间。但这个配置会被“持续时间”配置覆盖。
如果不想立即执行执行,可以通过调度器控制测试执行的开始时间和结束时间。
当测试开始时,如果设置了调度器,JMeter将等待直到到了开始时间。
在每个周期结束,JMeter将会检查是否到达结束时间,如果达到,停止运行;否则测试继续运行直到达到了重复限制。
jmeter压测过程中,TIME_WAIT很多导致请求数上不去问题解决
在实际施压的时候,发现请求量上不去,达不到压测效果。经定位发现,容器pod上存在大量TIME_WAIT,而实际在传输数据的连接远小于设置的并发线程数:
netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}'
为什么会有TIME_WAIT
这是TCP连接释放的4次挥手的过程:
- 主动关闭连接的一方,调用close();协议层发送FIN包
- 被动关闭的一方收到FIN包后,协议层回复ACK;然后被动关闭的一方,进入CLOSE_WAIT状态,主动关闭的一方等待对方关闭,则进入FIN_WAIT_2状态;此时,主动关闭的一方
- 等待
- 被动关闭一方的应用程序,调用close操作
- 被动关闭的一方在完成所有数据发送后,调用close()操作;此时,协议层发送FIN包给主动关闭的一方,等待对方的ACK,被动关闭的一方进入LAST_ACK状态;
- 主动关闭的一方收到FIN包,协议层回复ACK;此时,主动关闭连接的一方,进入TIME_WAIT状态;而被动关闭的一方,进入CLOSED状态
- 等待2MSL时间,主动关闭的一方,结束TIME_WAIT,进入CLOSED状态
这个过程可以得到一下几个信息:
- ESTABLISHED状态,表示正在发送请求的连接,即正在施压的请求个数
- 主动关闭连接的一方最终会进入TIME_WAIT状态
- TIME_WAIT会默认等待2MSL时间后,才最终进入CLOSED状态;
- 在一个连接没有进入CLOSED状态之前,这个连接是不能被重用
哪些情况会产生这么多TIME_WAIT,怎么处理
线程数确实很多,就可能会产生大量的TIME_WAIT
比如并行的线程数上万,由于一般是施压方主动断开连接,因此会积累大量的TIME_WAIT。建议解决方案:
- 建议使用分布式压测,将线程数分散到多台机器,这里可以使用云原生压测平台进行
jmeter的配置会影响TIME_WAIT的产生
-
建议开启该配置,使用长连接,这样会复用连接发送请求
-
Ramp-up时间(秒),这个配置表示多长时间把线程全部生成,需要根据业务情况做好配置,避免一次性生成太多配置,直接把施压机器搞垮,积累较多TIME_WAIT
-
Same user on each iteration,在 JMeter 中,user 就是线程,此选项的意思是说每个迭代都用相同的线程。它的影响就是单个线程多次迭代使用同一个线程,因为销毁和创建线程本身就会占用资源,可能会影响性能测试结果。建议开启
Linux本身没有设置回收使用TIME_WAIT状态的连接
如第二节中所述,TIME_WAIT状态的连接,需要2MSL时间后才能回收端口用于创建新的连接,但是实际Linux内核配置支持快速回收TIME_WAIT状态的连接,配置可查看:
cat /etc/sysctl.conf
- net.ipv4.tcp_tw_recycle,该配置表示快速回收TIME_WAIT连接,但在NAT网络下,会导致连接失败(刚好使用的就是NAT),另外Linux 从4.12内核版本开始移除了 tcp_tw_recycle 配置,我这里的机器是4.14,因此直接注释掉
- net.ipv4.tcp_tw_reuse,表示是否允许重新应用处于TIME-WAIT状态的socket用于新的TCP连接;那么,当连接被复用了之后,延迟或者重发的数据包到达,新的连接怎么判断,到达的数据是属于复用后的连接,还是复用前的连接呢?这就需要依赖net.ipv4.tcp_timestamps字段了。复用连接后,这条连接的时间被更新为当前的时间,当延迟的数据达到,延迟数据的时间是小于新连接的时间,所以,内核可以通过时间判断出,延迟的数据可以安全的丢弃掉了。
- net.ipv4.tcp_timestamps,在重用连接的情况下,该配置能帮助操作系统识别新来的数据是旧连接的还是新连接的
留言