本篇文章是 JMeter 官网的一篇文章--Best Practices。
16.最佳实践(Best Practices)
16.1使用最新版本的 JMeter
JMeter的性能正在不断提高,因此我们强烈鼓励用户使用最新的版本。
确保您始终阅读变更列表以了解新的改进和组件。您应该绝对避免使用在最后一个版本之前超过3个版本的版本。
16.2 使用正确的线程数
您的硬件功能以及测试计划设计都将影响使用JMeter可以有效运行的线程数量。这个数字还取决于您的服务器的速度(更快的服务器使JMeter工作更努力,因为它返回响应更快)。与任何负载测试工具一样,如果没有正确地确定线程的大小,就会遇到“协调遗漏”问题,这可能会导致错误或不准确的结果。如果需要大规模的负载测试,可以考虑使用分布式模式(或不使用分布式模式)在多台机器上运行多个非gui JMeter实例。在使用分布式模式时,在控制器节点上合并结果文件,如果使用多个自治实例,则可以合并示例结果文件进行后续分析。为了测试JMeter在给定平台上的表现,可以使用JavaTest采样器。它不需要任何网络访问,因此可以提供一些想法,以实现最大吞吐量。
JMeter有一个选项,可以延迟线程创建,直到线程开始采样,即在任何线程组延迟和线程本身的加速时间之后。这允许线程的总数非常大,前提是并发活动的线程不太多。
16.3Cookie管理器放在哪里
可以看我的另一篇文章创建一个 Web 测试计划。
16.4授权管理器放在哪里
可以看我的另一篇文章创建一个高级的 Web 测试计划。
16.5使用HTTP(S)测试脚本记录器
有关设置记录器的详细信息,请参阅HTTP(S)测试脚本记录器。最重要的是过滤掉所有你不感兴趣的请求。例如,没有必要记录图像请求(可以指示JMeter下载页面上的所有图像—参见HTTP请求)。这些只会打乱你的测试计划。最有可能的是,所有文件共享都有一个扩展名,例如.jsp、.asp、.php、.html或类似的格式。这些你应该“include”通过进入".*\.jsp"作为“包含模式(Include Pattern)”。
或者,您可以通过输入“.*\.gif”来排除图像作为“排除模式”。根据您的应用程序,这可能是,也可能不是更好的方法。您可能还需要排除样式表、javascript文件和其他包含的文件。测试您的设置,以验证您正在记录您想要的,然后删除和重新开始。
HTTP(S)测试脚本记录器期望找到一个带有记录控制器的ThreadGroup元素,它将在其中记录HTTP请求。这样可以方便地将所有示例打包到一个控制器中,控制器可以提供描述测试用例的名称。
现在,看看测试用例的步骤。如果您没有预定义的测试用例,使用JMeter来记录您的操作来定义您的测试用例。完成一系列步骤后,将整个测试用例保存在一个适当命名的文件中。然后,擦拭干净并开始一个新的测试用例。通过这样做,您可以快速地记录大量的测试用例“粗略的草稿”。
HTTP(S)测试脚本记录器最有用的特性之一是,您可以从记录的示例中抽象出某些公共元素。通过在测试计划级别或在用户定义变量元素中定义一些用户定义的变量,可以让JMeter自动替换记录样本中的值。例如,如果您在服务器“xxx.example.com”上测试一个应用程序,那么您可以定义一个名为“server”的变量,其值为“xxx.example.com”,并且在记录的示例中找到该值的任何位置都将被替换为“${server}”。
请注意匹配是大小写敏感的。
如果JMeter不记录任何示例,请检查浏览器是否真的在使用代理。如果浏览器工作正常,即使JMeter没有运行,那么浏览器就不能使用代理。一些浏览器忽略了localhost或127.0.0.1的代理设置;尝试使用本地主机名或IP。
“unknown_ca”错误可能意味着您正在尝试记录HTTPS,而浏览器尚未接受JMeter代理服务器证书。
16.6用户变量
有些测试计划需要为不同的用户/线程使用不同的值。例如,您可能想要测试一个序列,该序列需要每个用户的唯一登录。JMeter提供的工具很容易做到这一点。
比如:
创建一个包含用户名和密码的文本文件,用逗号分隔。将它放在与测试计划相同的目录中。
向测试计划添加CSV数据集配置元素。命名变量USER并PASS。
在适当的采样器上,用${USER}替换登录名,用${PASS}替换密码
CSV数据集元素将读取每个线程的新行。
16.7减少资源需求
关于减少资源使用的一些建议。
- 使用非GUI模式:jmeter -n -t test.jmx - l test.jtl
- 尽可能少使用监听器;如果使用上面的-l标志,它们都可以被删除或禁用。
- 在负载测试期间不要使用“查看结果树”或“用表格查看结果”监听器,只在脚本阶段使用它们调试脚本。
- 与其使用大量类似的采样器,不如在循环中使用相同的采样器,并使用变量(CSV数据集)来更改样本。[Include控制器在这里没有帮助,因为它将文件中的所有测试元素添加到测试计划中。]
- 不要使用函数测试模式(functional mode)
- 使用CSV输出而不是XML
- 只保存需要的数据
- 尽可能少使用断言
- 使用性能最好的脚本语言(请参阅JSR223部分)
如果您的测试需要大量的数据——尤其是需要随机处理的数据——在一个可以使用CSV数据集读取的文件中创建测试数据。这可以避免在运行时浪费资源。
16.8 BeanShell server
BeanShell解释器有一个非常有用的特性——它可以充当服务器,可以通过telnet或http访问。
不安全。任何可以连接到端口的人都可以发出任何BeanShell命令。它们可以提供对JMeter应用程序和主机的无限制访问。除非端口受到防火墙的保护,否则不要启用服务器。
如果您确实希望使用服务器,请在jmeter.properties中定义以下内容:
beanshell.server.port=9000
beanshell.server.file=../extras/startup.bsh
在上面的示例中,服务器将启动,并在端口9000和9001上监听。端口9000将用于http访问。端口9001将用于telnet访问。startup.bsh文件将由服务器处理,可以用来定义各种函数和设置变量。启动文件定义用于设置和打印JMeter和系统属性的方法。这是您应该在JMeter控制台看到的:
Startup script running
Startup script completed
Httpd started on port: 9000
Session started on port: 9001
有一个示例脚本(extras/remote.bsh)可以用来测试服务器。看看它是如何工作的。
注:该示例脚本所在的路径是:/Users/guxuecheng/Downloads/apache-jmeter-3.3/extras/remote.bsh
在JMeter bin目录中启动它时(如果从其他地方运行,则需要调整路径),输出应该如下所示:
$ java -jar ../lib/bshclient.jar localhost 9000 ../extras/remote.bsh
Connecting to BSH server on localhost:9000
Reading responses from server …
BeanShell 2.0b5 - by Pat Niemeyer ([email protected])
bsh % remote.bsh starting
user.home = C:\Documents and Settings\User
user.dir = D:\eclipseworkspaces\main\JMeter_trunk\bin
Setting property 'EXAMPLE' to '0'.
Setting property 'EXAMPLE' to '1'.
Setting property 'EXAMPLE' to '2'.
Setting property 'EXAMPLE' to '3'.
Setting property 'EXAMPLE' to '4'.
Setting property 'EXAMPLE' to '5'.
Setting property 'EXAMPLE' to '6'.
Setting property 'EXAMPLE' to '7'.
Setting property 'EXAMPLE' to '8'.
Setting property 'EXAMPLE' to '9'.
EXAMPLE = 9
remote.bsh ended
bsh % … disconnected from server.
作为一个实际示例,假设您有一个在非gui模式下运行的长时间运行的JMeter测试,并且您希望在测试期间的不同时间改变吞吐量。测试计划包括一个恒定吞吐量计时器,它是根据属性定义的,例如${__P(throughput)}。可以使用以下BeanShell命令来更改测试:
printprop("throughput");
curr = Integer.decode(args[0]); // Start value
inc = Integer.decode(args[1]); // Increment
end = Integer.decode(args[2]); // Final value
secs = Integer.decode(args[3]); // Wait between changes
while(curr <= end) {
setprop("throughput",curr.toString()); // Needs to be a string here
Thread.sleep(secs*1000);
curr += inc;
}
printprop("throughput");
脚本可以存储在一个文件中(throughput.bsh)。,并使用bshclient.jar发送到服务器。例如:
java -jar ../lib/bshclient.jar localhost 9000 throughput.bsh 70 5 100 60
16.9 BeanShell 脚本
从JMeter 3.1版本开始,我们建议从BeanShell切换到JSR223测试元素(更多细节请参阅下面的JSR223部分),并从__Beanshell函数切换到__groovy函数。
16.9.1 概述
每个BeanShell测试元素都有自己的解释器副本(针对每个线程)。如果测试元素被反复调用,例如在循环中,那么解释器在调用之间被保留,除非“Reset bsh.Interpreter before each call”选项被选中。
一些长时间运行的测试可能会导致解释器使用大量内存;如果是这种情况,尝试使用重置选项。
您可以使用命令行解释器在JMeter之外测试BeanShell脚本:
$ java -cp bsh-xxx.jar[;other jars as needed] bsh.Interpreter file.bsh [parameters]
or
$ java -cp bsh-xxx.jar bsh.Interpreter
bsh% source("file.bsh");
bsh% exit(); // or use EOF key (e.g. ^Z or ^D)
16.9.2 共享变量
变量可以在启动(初始化)脚本中定义。除非使用重置选项,否则这些选项将在测试元素的调用之间保留。
脚本还可以使用“vars”变量的get()和put()方法访问JMeter变量,例如:
vars.get("HOST");
vars.put("MSG","Successful");
get()和put()方法只支持具有字符串值的变量,但是也有getObject()和putObject()方法可以用于任意对象。JMeter变量是线程的局部变量,但是可以被所有测试元素(不仅仅是Beanshell)使用。
如果需要在线程之间共享变量,那么可以使用JMeter属性:
import org.apache.jmeter.util.JMeterUtils;
String value = JMeterUtils.getPropDefault("name","");
JMeterUtils.setProperty("name", "value");
示例.bshrc文件包含getprop()和setprop()方法的示例定义。
另一种共享变量的方法是使用“bsh.shared”共享名称空间。例如:
if (bsh.shared.myObj == void){
// not yet defined, so create it:
myObj = new AnyObject();
}
bsh.shared.myObj.process();
与其在测试元素中创建对象,还不如在JMeter属性“beanshell.init.file”定义的启动文件中创建对象。这只处理一次。
16.10 用Groovy或Jexl3等开发脚本函数。
作为函数编写和测试脚本是相当困难的。但是,JMeter有JSR223采样器,可以用任何支持它的语言替代它。我们建议使用Apache Groovy或任何支持JSR223可编译接口的语言。
创建一个包含JSR223采样器和树视图监听器的简单测试计划。在sampler script窗格中编写脚本,并通过运行测试来测试它。如果有任何错误,这些将显示在树视图和jmeter.log文件。同样,运行脚本的结果将作为响应显示。
一旦脚本正常工作,就可以将其存储为测试计划中的变量。然后可以使用脚本变量创建函数调用。例如,假设一个Groovy脚本存储在变量RANDOM_NAME中。然后,函数调用可以编码为${__groovy(${RANDOM_NAME})}。不需要转义脚本中的任何逗号,因为函数调用是在插入变量值之前解析的。
16.11 参数化测试(Parameterising tests)
通常,能够使用不同的设置重新运行相同的测试是很有用的。例如,更改线程或循环的数量,或更改主机名。
一种方法是在测试计划上定义一组变量,然后在测试元素中使用这些变量。例如,可以定义变量LOOPS=10,并在线程组中将其称为${LOOPS}。要使用20个循环运行测试,只需更改测试计划上的LOOPS变量的值。
如果您想要在非GUI模式下运行大量测试,那么这很快就会变得单调乏味。对此的一种解决方案是根据属性定义测试计划变量,例如LOOPS=${__P(loops,10))}。这将使用属性“loops”的值,如果没有找到属性,默认值为10。然后,可以在JMeter命令行上定义“loops”属性:
jmeter -Jloops = 12…
如果有很多属性需要一起更改,那么实现这一点的一种方法是使用一组属性文件。可以使用-q命令行选项将适当的属性文件传递到JMeter。
16.12 JSR223 元素
对于密集的负载测试,推荐的脚本语言是其脚本引擎实现可编译的接口语言。Groovy脚本引擎实现了编译。然而,从JMeter 3.1发布之日起,Beanshell和Javascript都没有这样做,因此建议避免使用它们进行密集的负载测试。
注意:Beanshell实现了可编译的接口,但是它没有被编码——方法只是抛出一个异常。JMeter对这个bug有一个明确的工作回合。
在使用JSR 223元素时,建议检查缓存编译后的脚本(如果可用的话),以确保如果底层语言支持的话,脚本编译被缓存。在这种情况下,确保脚本不使用任何使用${varName}的变量,因为缓存只接受${varName}的第一个值。而不是使用:
vars.get("varName")
您还可以将它们作为参数传递给脚本,并以这种方式使用它们。
16.13 在线程组和线程组之间共享变量
变量是线程的局部变量;在一个线程中设置的变量不能在另一个线程中读取。这是通过设计。对于在测试开始之前可以确定的变量,请参阅参数化测试(16.11)。如果直到测试开始才知道该值,则有多种选择:
- 将变量存储为属性——属性对JMeter实例是全局的
- 将变量写入文件并重新读取。
- 使用bsh.shared名称空间-参见16.9.2节
- 编写自己的Java类
16.14 管理属性
当您需要修改jmeter属性时,请确保您没有修改jmeter.properties文件,而不是从jmeter.properties复制属性并修改其在user.properties文件中的值。
这样做将简化迁移到JMeter的下一个版本。
请注意,在文档中jmeter.properties经常被提到,但这应该被理解为“将要修改的属性从jmeter.properties复制到user.properties,并在后面的文件中进行复制”。
user.properties文件取代jmeter.properties中定义的属性
16.15 弃用元素
建议不要使用废弃的元素(在“更改列表”和“组件引用”中标记为这样的元素),如果可用,请迁移到新建议的元素或以新的方式做同样的事情。
在版本N中,弃用元素从菜单中删除,但是可以通过修改user中的not_in_menu属性来进行迁移user.properties文件,并从中删除元素的完整类名。
请注意,版本N中废弃的元素将在版本N+1中被删除,因此请确保您尽快停止使用它们。