文章内容是参照Jmeter官网和自己实践完成的,JMeter官网地址贴上,有兴趣的朋友可以去阅读一下:JMeter官网
在介绍逻辑控制器之前,先说明一下测试计划和线程组这两个部件。
测试计划是使用 JMeter 进行测试的起点,它是其它 JMeter 测试元件的容器。一个Jmeter脚本中只能有一个测试计划。
线程组元件是一个测试计划的起点,测试计划的所有元件都要包含在线程组中。一个测试计划中可以有多个线程组。
Jmeter提供了多种逻辑控制器,下面将会对它们的作用和用法做详解。对于下文中多次使用的【BeanShell Sampler】,后续会详解其用法,这里先把它简单地看做能返回特定的值的一个请求即可。
简单控制器是最基本的控制器,对jmeter测试运行没有任何影响,可以将某些请求归集在一个简单控制器中,视为一个模块,使得脚本结构更清晰。
循环控制器,这个控制器的作用是使其子项循环运行。
循环次数(Loop Count):在输入框中输入需要循环的次数,控制器下的子项会循环相应的次数。如果勾选了【forever】,那么控制器下的子项会一直运行。
仅一次控制器,会使该控制器下的子项每个线程只运行一次,建立下面的脚本结构并运行:
结果如下:
对上面的脚本进行修改,加入循环控制器,修改线程组【线程数】为2,【循环次数】为2:
运行脚本,结果如下:
可见,【循环控制器】中的【仅一次控制器】依然生效。
交替控制器,使得该控制器包含的取样器步骤在每次循环中交替执行。例如下面的脚本结构,将线程组循环次数设为6并运行。
结果如下:
再看交替控制器下的两个参数项。
首先验证【Ignore sub-contorller blocks】的作用,建立如下脚本并运行(不勾选【Ignore sub-contorller blocks】):
结果如下:
勾选【Ignore sub-contorller blocks】后再次运行:
区别很明显,勾选了【Ignore sub-contorller blocks】后,交替控制器子控制器中的取样器一次运行只会被执行一个。
随机控制器,当该控制器下有多个子项时,每次循环会随机执行其中一个。建立下图的脚本结构,线程组【循环次数】设置为4。
运行脚本,结果如下:
随机控制器有一个参数项:Ignore sub-controller block(忽略子控制器模块)。如果勾选了此项,随机控制器下的子控制器中的多个子项只会被执行一个。
下面用实例验证一下。修改脚本结构:
线程组【循环次数】为2,运行脚本,结果:
勾选【Ignore sub-controller block】后再次运行脚本,结果变为:
随机顺序控制器,与简单控制器类似,会执行它下面的每个子项,只不过执行顺序是随机的。
吞吐量控制器,允许用户自行调整该控制器下的子项的执行频率。
吞吐量控制器有两种模式:
1、Total Executions:当该控制下的子项被执行固定数量后,停止吞吐量控制器。例如下面这个脚本,线程组【循环次数】设为6,运行脚本。
结果如下:
2、Percent Executions:百分比模式,该模式使吞吐量控制器下的子项执行总循环次数的一定比例(在吞吐量中设置该比例),例如下面的脚本。
设置线程组【循环次数】为60,运行后,查看聚合报告,吞吐量控制器下的HTTP请求1执行了30次,也就是(60*50%)次。
运行周期控制器,顾名思义,这是一种设置运行时间的控制器,它的效果就是使该控制器下的子项运行时间为【Runtime】中的数值(单位:s)。
不过,经过实测,如果线程组的循环次数勾选“永远”,则HTTP请求会一直运行,如果循环次数填入1,则HTTP请求会运行3s,循环次数填入2的话,HTTP请求运行6s,因此可知,在线程组不勾选“永远”的前提下,【Runtime Controller】的运行时间为【Runtime】的值乘以线程组循环次数。
If控制器,允许用户去设置该控制器下的子项是否运行。默认情况下,条件只在初始判断一次,但我们可以选择让它对控制器中包含的每个可运行项进行判断。
我们先看下【条件】这个输入项。它支持哪些方式:
其他变量表达式的示例:
判断循环控制器,作用是循环运行其子项,直到条件为false。这个控制器和Java中的while语法是很相似的。
【Condition】可以填入的值有:
下面做一个演示:
运行脚本,发现循环会一直进行下去。
如果将【BeanShell Sampler】中的语句改为“vars.put(“index”,“0”);”,运行发现脚本不进入while循环。
开关控制器,通过【Switch Value】来控制哪个子项被执行,作用和Java中的switch语法是很类似的。
【Switch Value】有两种赋值方式:索引和子项名,经过实际测试,如果填入数字,且子项中有以数字命名的子项(当然,实际工作中要尽量避免这种命名方式),索引优先生效。
下面通过一些实例进行说明:
遍历循环控制器,首先看下它的各输入项:
输入变量前缀:输入遍历需要的变量的前缀,图中是test,为什么要写“test”呢?这是因为【用户定义的变量】中变量名称是“test”为前缀的,前缀是指数字前面的内容。当然这个变量还可以来自【正则表达式提取器】、【参数化】等。
HTTP请求按下图写入,来验证ForEach Controller的作用。
运行脚本,发现HTTP请求被执行了三次(end-start的值),${test}依次被赋值test1、test2、test3。
再看下面这个例子,添加BeanShell Sampler,并给它添加一个正则表达式提取器,效果就是提取到一个名为“test_1”,值为“bcde”的变量到内存中。HTTP请求的内容不变(BeanShell和正则表达式见,这里的BeanShell其实就是提供了一个模拟接口,返回值是return的内容,即“abcdef”,而正则表达式提取器的作用是按规则提取返回内容的一部分)。
运行脚本,可以看到“bcde”的值被引用过来了。
模块控制器,可以理解为对封装好的模块的调用。
观察上图的脚本结构并运行,查看结果树,可以看到,线程组1中的模块控制器可以调用线程组2中的简单控制器3及其下面的sampler。
由此可知,模块控制器的作用在于,当一个测试片段(通常是一个包含sampler的控制器)在脚本中多处运行时,模块控制器可以非常便利地完成调用,避免重写这个测试片段,使脚本减少冗余,结构简洁。
另外,当测试计划中有多个线程组时,一个线程组需要运行其它线程组的一个测试片段,模块控制器的作用就更加明显了。在这种场景下,即使其它线程组被禁用,依然不影响模块控制器对其节点下测试片段的调用。而在实际测试工作中,通常是一个线程组启用,而其它线程组被禁用,防止线程组互相干扰。
使用模块控制器时,需要注意的是,要保证控制器的名字各不相同,因为模块控制器是通过控制器名去调用的。
包含控制器,它的作用是引入外部的jmx文件。需要注意的有以下几点:
事务控制器,生成一个额外的采样器来测量其下测试元素的总体时间;值得注意的是,这个时间包含该控制器范围内的所有处理时间,而不仅仅是采样器的。由于时钟误差,而事务控制器的总体用时可能会稍微大于事务控制器下各个子项用时之和。
它有两个参数项:
建立以下结构的脚本:
【BeanShell PreProcessor】中写入以下语句,它的作用是使HTTP请求1执行前等待2000ms(BeanShell PreProcessor会在后面Beanshell专题中详细讲解)。
运行脚本,查看结果树和聚合报告:
可以看到聚合报告中记录了【事务处理器】的响应用时信息。我们勾选了【Generate parent sample】后再次运行,我们发现结果树和聚合报告都有了变化,结果树中依然能看到HTTP请求,但已经归集到事务控制器下,而聚合报告中不再显示取样器。
我们再勾选【include duration of timer and pre-post processors in generated sample】后运行脚本,区别就是聚合报告中事务控制器响应时间包含了PreProcessor的时间(2000ms)。
临界区控制器,这个名字听起来很难理解,其实这个控制器的作用是为它的子项加一个同步锁,使得在多线程场景下,同一时刻,只有一个线程能够调用其子项。我们用实际操作来验证一下它的作用。建立如下图的脚本结构:
然后设置线程组线程数为5,循环次数为2,设置固定定时器线程延迟为1000ms(固定定时器介绍见后文,这里定时器的作用是使每次HTTP请求先等待1s),而HTTP2请求是空的,目的是让HTTP请求和固定定时器的单次整体用时为1s。
运行后,观察结果树和聚合报告,可以观察到,HTTP请求是1s中被执行一次(HTTP请求是空请求,本身几乎不耗时,但由于固定定时器的存在,HTTP请求的单次用时是1s),因此Critical Section Controller的线程同步锁作用得到验证。
我们改变脚本结构,可以看到HTTP请求2同一时刻会被多个线程调用,tps也得以提升。
权重开关控制器(直译),它能分配其子项目(Child Item)的权重,从而控制子项的执行概率。首先建立如下的脚本结构:
在bzm - Weighted Switch Controller下有两个HTTP请求,将它们的Weight设置为7和3,线程组循环次数设为100,当脚本运行结束后,观察聚合报告,可以看到,HTTP请求1和HTTP请求2分别执行了70次和30次。经过多次测试,这个权重是精确控制,而非概率性控制。
Weighted Switch Controller配合其他控制器,会有更丰富的用法,比如以简单控制器、循环控制器作为子项,甚至以自身作为子项,这里不再赘述,感兴趣的朋友可以动手做下测试。