性能测试:模拟多个用户的操作对服务器硬件性能的影响
TPS:Transaction per Second,每秒事务处理能力
RT:Response Time,响应时间
安装
由于本人只有window系统,故只讲解win下的安装
安装JDK
下载地址:https://www.oracle.com/java/technologies/downloads/archive/#JavaSE
根据系统选择安装包
下载成功后,直接双击,一直下一步即可,安装好之后,配置环境变量
JAVA_HOME:jdk安装路径
PATH:%JAVA_HOME%\bin
配置好之后,打开cmd,输入:java -version
能正常打印java版本,表示安装完成
安装Jmeter
下载地址:https://jmeter.apache.org/download_jmeter.cgi
下载后,解压缩即可
简单示例
在Jmeter解压缩的bin目录下,找到jmeter的windows批处理文件,双击,会打开一个cmd窗口(不要关)和Jmeter界面
先来一个比较简单的示范:
配置Thread Group
界面默认展示一个Test Plan,在测试计划下添加一个线程组。JMeter是由Java实现的,并且使用一个线程来模拟一个用户,所以线程组(Thread Group)可以理解为一组用户
操作路径:右键 Test Plan > Add > Threads(Users) > Thread Group
在Thread Group中,重点关注线程属性Thread Properties:
Number of Threads(users):线程数,也可理解为虚拟用户数,默认为1;
Ramp-up period(seconds):启动所有线程的时间。如果Ramp-up period设置为T秒,线程数Number of Threads为N个,则Jemeter将每隔T/N秒建立一个线程;比如线程数为10,线程时间为100,则表示要在100秒内启动并运行10个线程,每个线程会在上一个线程启动后10秒(100秒/10个)启动;为0时表示并发
Loop Count:循环次数,每个线程执行的次数。比如线程数Number of Threads为10,Loop Count为3,则表示每个线程都要执行3次,即共发送10*3个请求。如果选中了“Infinite”(永远),则除非强制停止,否则会一直循环执行。
Jmeter创建线程数量除了受Number of Threads和Ramp-up period影响外,还受本地设备性能的影响,比如本地只能1秒启动10个线程,而我们如果设置了1秒启动20个线程,设备做不到,就只能延迟创建了,而且如果设置的太大,Jmeter判断内存无法在规定时间内启动符合预期的线程数,就会直接报错。
配置HTTP Request和View Results Tree
确定好用户执行情况后,就需要安排用户的执行内容:
Jmeter用来测试服务器性能,服务器一般是提供接口来提供数据,所以以https的请求作为例子进行讲解:
接口信息:
请求方式:Get
URL:https://httpbin.org/get
说明:该接口的响应报文默认返回请求报文的请求头、URL、请求的IP和上传的参数,上传的参数存放在键为args下
添加HTTP Request组件路径:右键 Thread Group> Add > Sampler > HTTP Request
HTTP Request配置OK后,即可运行
运行时可能会让你保存,点击保存即可,点击绿色三角形后,会置灰不可点,执行完后会恢复为绿色,旁边的STOP按钮在执行时会显示红色,执行完成再恢复成灰色
执行后,并没有看到什么执行结果或日志之类的,是因为刚安装的JMeter还没设置,我们可以先打开日志查看执行进度
操作路径:Options > Log Viewer,将Log Viewer勾选
Log Viewer勾选后,在主界面的右下方会有个窗口打印日志
日志只能查看执行进度,无法查看执行情况,如果确实想查看执行结果,需要配置监听器View Results Tree
操作路径:右击Thread Group > Add > Listener > View Results Tree
添加View Results Tree成功后,再次执行(点击绿色三角形),执行完成后,可以在View Results Tree组件查看执行结果
如果请求执行和校验(校验的部分在章节后面)没有异常,则会绿色图标显示,如果有异常,则红色图标显示。从红框中可以看出,一共发送了6次请求(线程组设置了Number of Threads为3,Loop Count为2,所以请求次数总数为3*2=6),点击相关的请求,可以查看请求数据和响应数据
配置Response Assertion和Assetion Results
虽然从View Results Tree可以看到请求执行成功了,但是无法确定响应报文是否符合预期的,所以如果要校验数据是否符合预期,可以配置断言组件,本示例简单处理,配置响应报文断言组件Response Assertion,对响应报文中的age和name进行校验
操作路径:右击 Http Request(该例子中的httpbin测试) > Add > Assertions > Response Assertion
关于部分字段介绍:
- Apply to:默认Main Sample only,主要的取样器,一般很少改
- Field to Test:默认Text Response,即根据响应报文去断言,其他的有Response Code响应码、Response Message响应消息(比如OK)、Response Headers响应头
- Pattern Matching Rules:模式匹配规则
- Contains:如果被校验内容包含给定的正则表达式模式或字符串,则为true
- Matches:如果整个被校验内容匹配给定的正则表达式模式,则为true
- Equals:如果整个被校验内容等于给定的正则表达式模式,则为true
- Substring:如果被校验内容包含给定的字符串,则为true
- Not:匹配非给定模式
- Or:或,对Patterns to Test的内容进行“或”运算
Field to Test选择Text Response,Pattern Matching Rules选择Substring,Patterns to Test输入要匹配的字符串,配置好响应报文断言后,还需要配置Assetion Results来查看断言结果
操作路径:右击 Thread Group > Add > Listener > Assetion Results
添加成功后,再次执行线程组内容,执行结果如下图,如果断言成功,则Assetion Results的请求数据展示中,右侧界面为空
如果断言失败,则Assetion Results的请求数据展示中,右侧界面展示失败描述
为了模拟断言失败的情况,将Response Assertion的Patterns to Test第一个改成:"age": "19"
执行线程组,查看Assetion Results
在界面右侧展示校验失败的具体情况
配置User Defined Variables
在 配置HTTP Request和View Results Tree 和配置Response Assertion和Assetion Results 章节中我们可以看到,响应报文中的age和name是跟请求参数保持一致的,如果我们修改了请求参数,则响应报文的字段校验也需要同步修改,为了解决这个问题,我们需要定义用户变量,HTTP Request组件和Response Assertion使用用户变量,这样需要修改参数的时候,只需要修改用户变量对应的值
User Defined Variables 可以在Thread Group或Http Request下添加
操作路径:右键 Thread Group或Http Request > Add > Config Elemen > User Defined Variables
添加后,配置变量,点击窗口下方的 Add 按钮,name一列输入变量名称,Value列输入对应变量的值,可以在Description列对变量的使用进行说明
User Defined Variables配置变量完成后,在相关位置使用变量,本示例只有HTTP Request和Response Assertion这2个地方需要用到,故只需修改这2个组件对应内容,变量的使用格式为:${变量名}
执行后,查看结果:
从Assetion Results的结果来看,校验失败了,并没有包含"name": "温小八"
这个字符串,所以我们先去查看响应报文是不是没有包含
从View Results Tree的Response data中,可以看到,name字段并不是显示"温小八",而是Unicode字符串(\u开头就是Unicode编码字符串),将"\u6e29\u5c0f\u516b"转成中文,即为“温小八”
响应报文是被测服务器(即接口)返回的,要对响应报文的Unicode字符串做处理,我们可以添加后置处理器,专门对响应报文中的Unicode进行处理进行处理
操作路径:右键 Thread Group > Add > Post Processors > BeanShell PostProcessor
BeanShell PostProcessor的脚本内容如下:
String s = new String(prev.getResponseData(), "ISO-8859-1");// ISO-8859-1是Jmeter默认的编码,需要保持跟Jmeter安装目录下bin\jmeter.properties文件中的sampleresult.default.encoding的值一致
char aChar;
int len = s.length();
StringBuffer outBuffer = new StringBuffer(len);
for (int x = 0; x < len;) {
aChar = s.charAt(x++);
if (aChar == '\\') {
aChar = s.charAt(x++);
if (aChar == 'u') {
int value = 0;
for (int i = 0; i < 4; i++) {
aChar = s.charAt(x++);
switch (aChar) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
value = (value << 4) + aChar - '0';
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
value = (value << 4) + 10 + aChar - 'a';
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
value = (value << 4) + 10 + aChar - 'A';
break;
default:
throw new IllegalArgumentException(
"Malformed \\uxxxx encoding.");
}
}
outBuffer.append((char) value);
} else {
if (aChar == 't')
aChar = '\t';
else if (aChar == 'r')
aChar = '\r';
else if (aChar == 'n')
aChar = '\n';
else if (aChar == 'f')
aChar = '\f';
outBuffer.append(aChar);
}
} else
outBuffer.append(aChar);
}
prev.setResponseData(outBuffer.toString());
再次执行,校验成功
在View Results Tree的Response data中也可以看到,响应报文中的Unicode字符串被处理成了中文
中文处理问题,参考了该篇博客:JMeter中响应数据Unicode编码转换成中文
关联
在测试过程中,我们经常会遇到说后续的请求跟前一个请求的响应报文内容有关的,比如想要取消关注,则需要先登录、关注了,才能取消关注,即后面的请求依赖于前一个请求。
以下以深圳图书馆搜索图书为例,想要获取某本书的具体信息,需要先根据书名进行搜索,然后再进入书的详情页面:
-
搜索图书:https://www.szlib.org.cn/opac/
- 对应接口:https://szlib.org.cn/api/opacservice/getQueryResult?v_index=title&v_value=${书名}+&library=all&v_tablearray=bibliosm,serbibm,apabibibm,mmbibm,&sortfield=ptitle&sorttype=desc&pageNum=1&v_page=1
- 说明:从接口中分析,v_value为对应的搜索内容
-
书本详情页面:https://szlib.org.cn/opac/searchDetail?tablename=bibliosm&recordid=686065
- 对应接口:https://szlib.org.cn/api/opacservice/getBookDetail?metaTable=bibliosm&metaId=${recordid}
- 说明:接口中的${recordid}为搜索页面接口响应报文中的recordid字段的值
分析了下,我们可以根据书名模糊搜索对应书本,获取响应报文里的recordid字段,然后书本详情页面接口中,recordid内容赋值给metaId参数并上传,获取书本详情信息。
所以先配置搜索图书的请求:
请求执行正常,由于是从搜索页面的响应报文获取数据,所以需要在搜索图书请求中增加提取器(Extractor)
响应报文是Json字符串,所以选择了了JSON Extractor提取器
JSON Path expression的语法如下:
符号 | 描述 |
---|---|
$ | 根节点 |
@ | 当前节点 |
.or[] | 子节点 |
… | 所有符合条件的节点 |
* | 所有节点 |
[] | 迭代器标示,如数组下标 |
[,] | 支持迭代器做多选 |
[start:end :step] | 数组切片运算符 |
?() | 过滤 |
() | 表达式计算 |
书本详情页面的HTTP Request组件配置如下:
执行结果如下图: