Jmeter部署及使用
Apache JMeter™ 应用程序是Apache 组织基于Java开发的压力测试工具, 100% 纯 Java 应用程序。JMeter 最初被设计用于 Web 应用测试,但后来扩展到了其他测试领域,可用于测试静态和动态资源,JMeter 可对服务器、网络或对象模拟巨大的负载,在不同压力类别下测试它们的强度和分析整体性能。得益于Java社区及其架构设计,Jmeter扩展性极强。
简述
记录Linux环境下部署
Jmeter
及相关使用。JMeter 与 Java 8 或更高版本兼容。 出于安全和性能原因,官方强烈建议安装主要版本的最新次要Java版本。官方文档 Apache JMeter - 用户手册部署
配置Java环境(必须)
下载页面Apache JMeter - 下载 Apache JMeter
1
$ wget https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.6.3.tgz
解压
1
$ tar -zxvf apache-jmeter-5.6.3.tgz
配置环境变量
1
2
3
4
5
6
7
8$ vim /etc/profile
# 追加以下内容
export JMETER_HOME=/data/apache-jmeter-5.6.3
export CLASSPATH=${JMETER_HOME}/lib/ext/ApacheJMeter_core.jar:${JMETER_HOME}/lib/jorphan.jar:${CLASSPATH}
export PATH=${JMETER_HOME}/bin:$PATH
$ source /etc/profile
相关操作处理
问题:rmi_keystore.jks (No such file or directory)
Server failed to start: java.rmi.server.ExportException: Listen failed on port: 0; nested exception is:
java.io.FileNotFoundException: rmi_keystore.jks (No such file or directory)
An error occurred: Listen failed on port: 0; nested exception is:
java.io.FileNotFoundException: rmi_keystore.jks (No such file or directory)处理方式:
1
2
3
4$ vim /data/apache-jmeter-5.6.3/bin/jmeter.properties
# 禁用ssl
server.rmi.ssl.disable=true格式乱码
1
2
3
4$ vim /data/apache-jmeter-5.6.3/bin/jmeter.properties
# 修改格式utf-8
sampleresult.default.encoding=UTF-8多节点
- 确保所有从节点安装相同版本的JMeter
- 防火墙开放端口(默认1099)
1
2
3
4
5
6
7
8$ vim /data/apache-jmeter-5.6.3/bin/jmeter.properties
remote_hosts=192.168.1.101:1099,192.168.1.102:1099
# node节点执行
$ jmeter-server
# 控制节点
$ jmeter -n -t test.jmx -r -l test.jtl -e -o testresult
命令行参数
参数 说明 示例 -n
非GUI模式(必须参数) jmeter -n
-t
指定测试计划文件(.jmx) jmeter -n -t test.jmx
-l
指定结果日志文件(.jtl或.csv) jmeter -n -t test.jmx -l result.jtl
-j
指定JMeter日志文件 jmeter -j jmeter.log
-p
指定JMeter属性文件 jmeter -n -p config.properties
-q
指定用户自定义属性文件 jmeter -n -q user.properties
-L
设置日志级别(DEBUG/INFO等) jmeter -L DEBUG
-S
从远程服务器加载属性文件 jmeter -S 192.168.1.100:8000/config.properties
-r
启动远程服务器(分布式模式) jmeter -n -r
-R
指定远程服务器列表 jmeter -n -R 192.168.1.101,192.168.1.102
-d
指定JMeter home目录(用于从节点) jmeter -d /opt/jmeter
-X
强制退出远程服务器 jmeter -n -X
-G
设置全局属性(覆盖配置文件) jmeter -Gthreads=50
-J
设置JMeter属性(同GUI中的 __P()
函数)jmeter -Jramp.up=10
-D
设置系统属性(Java系统属性) jmeter -Djava.net.preferIPv4Stack=true
-e
测试后生成HTML报告 jmeter -n -t test.jmx -l result.jtl -e -o report
-o
指定HTML报告输出目录,指定的文件及文件夹,必须不存在 jmeter -o ./report
常用组合命令
基础性能测试
1
jmeter -n -t test.jmx -l result.jtl -j jmeter.log -e -o report
分布式测试:
1
jmeter -n -t test.jmx -R 192.168.1.101,192.168.1.102 -l result.jtl
带参数化测试:
1
jmeter -n -t test.jmx -Jusers=100 -Jramp.up=60 -l result.jtl
生成Dashboard报告:
1
jmeter -g result.jtl -o report
常用函数
Request
、Response
对象1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56// 获取请求内容
log.info("Request: " + prev.getSamplerData());
// 获取响应内容
log.info("Response: " + prev.getResponseDataAsString());
// 获取请求名
log.info("Thread Name: " + prev.getThreadName());
// 开始采样时间
log.info("Sample Start: " + (new Date(prev.getStartTime())).toString());
// 当前 Sampler 的加载时间,单位 ms
log.info("Load Time: " + prev.getTime().toString());
// 连接时间,单位 ms
log.info("Connect Time: " + prev.getConnectTime().toString());
// 延时
log.info("Latency: " + prev.getLatency().toString());
// 响应中的字节数
log.info("Size in bytes: " + prev.getBytesAsLong().toString());
// 发送的字节数
log.info("Sent bytes: " + prev.getSentBytes().toString());
// 响应 header 的字节数
log.info("Header size in bytes: " + prev.getHeadersSize().toString());
// 响应 body 的字节数
log.info("Body size in bytes: " + prev.getBodySizeAsLong().toString());
// 合并 Sample 个数
log.info("Sample Count: " + prev.getSampleCount().toString());
// 发生错误个数
log.info("Error Count: " + prev.getErrorCount().toString());
// 返回数据格式
log.info('Data type("text"|"bin"|""): ' + prev.getDataType().toString());
// 响应状态码
log.info("Response Code: " + prev.getResponseCode().toString());
// 响应消息
log.info("Response Message: " + prev.getResponseMessage().toString());
// 响应的Header
log.info("Response Header: " + prev.getResponseHeaders().toString());
// 响应的内容类型
log.info("ContentType: " + prev.getContentType().toString());
// 响应的数据编码
log.info("DataEncoding: " + prev.getDataEncodingNoDefault().toString());打印Jmeter响应内容(Response)至log,并断言
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18String clue_msg="login_info"; //提示信息分割线
String Str = "{\"code\":1000"; //期望响应内容(包含即可)
String response = "";
response = prev.getResponseDataAsString();
if(response == ""){
Failure = true;
FailureMessage = "系统无响应,获取不到响应数据!";
log.info(FailureMessage);
}
else if(response.contains(Str) == false){
Failure = true;
String Msg = "\n ---------------------------------------------"+clue_msg+"---------------------------------------------";
FailureMessage = Msg + "\n" + "期望结果: " + Str + "\n响应内容: " + response + "\n";
log.info(FailureMessage);
}打印响应时间至log,并断言
1
2
3
4
5
6
7
8String clue_msg="login_info";
//期望响应时间 ms
int expectation_ms = 100;
int actual_ms = Integer.parseInt(prev.getTime().toString());
if(actual_ms>expectation_ms){
time_log = timeMsg + "\n" + "期望响应时间: " + expectation_ms + "ms \n实际响应时间: " + actual_ms + "ms\n";
log.info(time_log);
}操作本地文件
1
2
3
4
5
6
7
8
9
10
11
FileWriter fstream = new FileWriter("./testresult/${__time(yyyyMMdd,)}.txt",true);
//"./testresult/${__time(yyyyMMdd,)}.txt" 写入路径,可以用相对路径,也可以是绝对路径,根据个人实际情况选择,后面的参数 ‘true’,代表追加写入
BufferedWriter out = new BufferedWriter(fstream);
out.write(vars.get("group_id")+"\n");
// group_id 是Jmeter中自己定义的变量,"\n" 代表换行 ","相当于Tab键
out.close();
fstream.close();Unicode转UTF-8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62BeanShell--UTF-8
//获取响应代码Unicode编码的
String s2=new String(prev.getResponseData(),"UTF-8");
//---------------一下步骤为转码过程---------------
char aChar;
int len= s2.length();
StringBuffer outBuffer=new StringBuffer(len);
for(int x =0; x <len;){
aChar= s2.charAt(x++);
if(aChar=='\\'){
aChar= s2.charAt(x++);
if(aChar=='u'){
int value =0;
for(int i=0;i<4;i++){
aChar= s2.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());变量自增
1
2
3
4
5
6
7
8
9
10
11
12
13# 传入参数 while_count 并获取值
while_count=vars.get("while_count");
# java是强类型 说明下 格式
int while_count_int= new Long(while_count).intValue();
# 执行 i++ 自加一操作
while_count_int++;
# 将 +1 后的参数传出
vars.put("while_count",while_count_int+"")
# while_count++