一、使用Jmeter分布式测试的背景
1、GUI、非GUI(即命令行)的区别
Jmeter有两种运行方式,GUI、非GUI。
GUI:在Windows系统上运行,图形化界面,方便收集测试结果,但是消耗压力机资源较高,容易卡死,有并发限制。
非GUI:在Linux系统上通过命令行运行,无图形化界面,不方便收集测试结果,但是占用压力机资源较低,可以支持较大并发。
2、GUI遇到的问题
1)图形化界面消耗更多资源,如CPU和内存,容易使压力机达到瓶颈,从而影响测试结果。
Jmeter是基于java程序运行的,在windows上使用Jmeter进行性能测试时,非常耗费客户机的CPU和内存,如果并发数稍微大一点(比如100、1000...并发),单台电脑的配置经常无法支持,很容易卡死,即使不卡死也会使电脑运行很慢,导致我们没办法进行其它操作。
2)图形化界面不支持大型的负载测试和性能测试,并发较大时,jmeter会崩溃。
综上,jmeter压测时需要使用非GUI即命令行执行,减少资源消耗。
3、非GUI单点遇到的问题
在使用Jmeter进行性能测试时,在不进行调优的情况下,并发用户数很难突破1000;Jmeter进行调优后,单台并发数可以突破2000。但是如果要支持更大的并发,例如:5000、20000并发,则需要使用Jmeter提供的分布式测试的功能。
二、Jmeter分布式执行原理
1、Jmeter分布式测试时,选择其中一台作为控制机(master),其它机器作为执行机(slave)。
建议:一台linux作为master,多台linux作为slave。主要是避免slave压测机数据上报带来的影响。要求:slaver服务的jdk和Jmeter版本要一致,避免出现一些疑难杂症。
2、执行时,master会把脚本发送到每台slave上,slave拿到脚本后就开始执行,slave执行时不需要启动GUI,如果引用到csv等外部的文件,则每台slave所在的机器都需要相应位置(脚本中可以指定文件路径)手工上传该文件。slave本地不需预先存储脚本,但是需要有脚本中的依赖文件(如csv文件等),并需要设置正确的路径。
3、执行完成后,slave会把结果回传给master,master会收集所有slave的信息并汇总。
注意:
(1)每一台jmeter远程服务器(slave机器)都执行相同的测试计划,jmeter不会在执行期间做负载均衡,每一台服务器都会完整地运行测试计划;
(2)采用JMeter远程模式并不会比独立运行相同数目的非GUI测试更耗费资源。但是,如果使用大量的JMeter远程服务器,可能会导致客户端过载,或者网络连接发生拥塞;
(3)默认情况下,master机器是不执行参与生成并发数据的;
(4)调度机(master)和执行机(slave)最好分开,由于master需要发送信息给slave并且会接收slave回传回来的测试数据,所以mater自身会有消耗,所以建议单独用一台机器作为mater。
三、Jmeter分布式设备资源准备(根据并发数量准备,此处仅供参考)
2台16核32G linux环境Centos7.6(代理机),1台8核16Glinux环境(控制机),linux系统版本由jmeter版本兼容环境决定,本次使用的系统是Cenos7.6,Jmeter版本5.2.1,JDK版本1.8。
3台机器分别各自安装好jdk、jmeter。
四、Jmeter分布式搭建
1台作为控制机,2台作为从机(执行机)。
1、JDK1.8安装
1)官网下载文件
下载链接:https://www.oracle.com/java/technologies/javase-jdk8-downloads.html
本次使用的安装包:jdk-8u91-linux-x64.tar.gz
2)上传文件解压
进入安装包目录执行解压命令:tar -xzvf jdk-8u91-linux-x64.tar.gz
3)linux环境变量配置
执行命令vi /etc/profile,在文件底部增加如下内容(安装路径根据实际情况修改),并保存:
JAVA_HOME=/usr/local/jdk1.8
export JAVA_HOME
JRE_HOME=\$JAVA_HOME/jre
export JRE_HOME
PATH=\$JAVA_HOME/bin:\$JRE_HOME:bin:\$PATH
export PATH
CLASSPATH=\$JAVA_HOME/lib/dt.jar:\$JAVA_HOME/lib/tool.jar:\$CLASSPATH
export CLASSPATH
执行命令source /etc/profile ,生效文件修改。
4)验证jdk安装是否成功
执行命令java -version,显示java版本信息则表示安装成功
2、Jmeter安装
1)官网下载文件
下载链接:http://jmeter.apache.org/download_jmeter.cgi 或 链接: https://pan.baidu.com/s/1mQ1y8yh0pEU8_tzyxv7zww 提取码: 33ts
本次使用的安装包:apache-jmeter-5.2.1.tgz
2)上传文件并解压
进入安装包目录执行解压命令:tar zxvf apache-jmeter-5.2.1.tgz
3)linux环境变量配置
执行命令vi /etc/profile,在文件底部增加如下内容,并保存:
export JMETER_HOME=/opt/apache-jmeter-5.2.1
export CLASSPATH=${JMETER_HOME}/lib/ext/ApacheJMeter_core.jar:${JMETER_HOME}/lib/jorphan.jar:${CLASSPATH}
export PATH=${JMETER_HOME}/bin:${PATH}
执行命令source /etc/profile ,生效文件修改。
4)验证jmeter安装是否成功
执行命令jmeter -v,显示jmeter版本信息则表示安装成功
5)服务授权
如果压测启动和服务配置都是root权限,需要在linux环境下给jmeter和jmeter-server授权,命令:chmod 777 jmeter
3、jmeter控制机配置文件修改
1)修改jmeter.properties中的remote_hosts
文件路径:jmeter安装路径的bin目录下
修改点1:文件中remote_hosts值,原文件是remote_hosts=127.0.0.1,修改成remote_hosts=134.175.16.161:1099,193.112.110.50:1099,此处IP是代理机IP地址(腾讯云机器此处写内网IP)和端口按默认值则填写1099或直接写IP即可。
修改点2:文件中server.rmi.ssl.disable值,原文件是#server.rmi.ssl.disable=false,修改成server.rmi.ssl.disable=true。若未修改则启动执行时会报错:java.io.FileNotFoundException: rmi_keystore.jks (No such file or directory),如下所示:
2)内存修改
方法一(推荐):修改jmeter,修改后启动命令直接使用jmeter
使用UE软件打开该文件搜索HEAP修改最大值
: "${HEAP:="-Xms4g -Xmx8g -XX:MaxMetaspaceSize=512m"}"
方法二:修改jmeter.sh,修改后启动命令需使用jmeter.sh
文件底部增加以下内容:
java $JVM_ARGS -Xms1G -Xmx3G -XX:MaxPermSize=512m -Dapple.laf.useScreenMenuBar=true -jar dirname $0
/ApacheJMeter.jar "$@"
4、jmeter执行机(slave)配置文件修改
slave执行机与控制机配置文件修改基本一样,不同之处在于控制机需要修改jmeter.properties文件中remote_hosts的值,添加各个执行机IP地址。
1)修改jmeter.properties
文件路径:jmeter安装路径的bin目录下
修改点1:文件中server.rmi.ssl.disable值,原文件是#server.rmi.ssl.disable=false,修改成server.rmi.ssl.disable=true
2)内存修改(参照控制机修改方法)
3)slave执行机端优化:linux端口数量调整
vim /etc/sysctl.conf 添加
net.ipv4.ip_local_port_range = 1024 65000
fs.file-max = 6553560
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_intvl = 3
net.core.netdev_max_backlog = 3000
net.core.somaxconn = 3000
net.ipv4.tcp_keepalive_time = 1800
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_rmem = 8192 4194304 8388608
net.ipv4.tcp_wmem = 8192 4194304 8388608
net.core.rmem_max = 8388608
net.core.wmem_max = 8388608
net.ipv4.tcp_timestamps = 1
sysctl.conf文件修改生效命令:sysctl -p
若未做此优化,会出现较多如下报错:
Non HTTP response code: java.net.NoRouteToHostException
4)slave执行机关闭防火墙
分布式执行出现报错”No route to host“时需要slave执行机关闭防火墙
查看防火墙状态(CentOS7.6需使用命令systemctl status firewalld)
service iptables status/systemctl status firewalld
关闭防火墙
systemctl stop firewalld
5、jmeter的slave执行机服务启动(两台都分别启动)
启动命令:
jmeter-server -Djava.rmi.server.hostname=134.175.16.161 (slave执行机本机IP) ,注意点:如果是配置内网ip,此处ip为该slave执行机对应的内网ip地址
启动命令若未加入本机IP,可能会出现“An error occurred: Cannot start. localhost is a loopback address”错误,若想直接通过命令jmeter-server启动不加入IP可修改文件jmeter-server增加内容:RMI_HOST_DEF=-Djava.rmi.server.hostname=134.175.16.161
备注:后台运行执行时,可在命令前面加入nohup,命令末尾加入&,例:nohup jmeter-server -Djava.rmi.server.hostname=134.175.16.161 &
启动成功标志如下:
6、上传压测脚本和外部读取的csv文件
只需要上传压测脚本到控制机master服务器,节点机slave不需要上传,压测脚本自动回发到slaver执行机上;但是如果脚本中涉及从外部读取的csv文件,那该文件就需要上传到各个slaver执行机上。
注意:参数化文件路径若只写文件名称无具体路径时,linux环境下默认读取用户目录下的文件(例:登录用户是root,则是在root目录下),windows环境默认读取的是跟脚本同目录的路径。
7、jmeter控制机命令启动执行
1)命令行格式
常用格式(单机执行命令):
jmeter -n -t [jmx脚本路径] -l [测试输出结果文件路径] -e -o [结果文件html格式存放路径] -j [测试输出结果日志]
示例:
jmeter -n -t /opt/testplan/xssb.jmx -l /opt/testplan/result_xssb_200user_01.jtl -e -o /opt/testplan/result_xssb_200user_01/ -j /opt/testplan/result_xssb_200user_01.log
分布式执行,启动所有slave机执行命令:
jmeter -n -t [jmx脚本路径] -r -l [测试输出结果文件路径] -j [测试输出结果日志]
例:jmeter -n -t /opt/testplan/xssb.jmx -r -l /opt/testplan/result_xssb_200.jtl -j /opt/testplan/result_xssb_200.log
分布式执行,启动指定slave机执行命令:
jmeter -n -t [jmx脚本路径] -R ip1,ip2 -l [测试输出结果文件路径] -j [测试输出结果日志]
例:jmeter -n -t /opt/testplanHn/count.jmx -R 172.16.0.12,172.16.0.7 -l /opt/testplanHn/count88.jtl -j /opt/testplanHn/result_count88.log
(不推荐)启动所有slave机执行命令并将结果转成html报告格式:
jmeter -n -t [jmx脚本路径] -r -l [测试输出结果文件路径] -e -o [结果文件html格式存放路径] -j [测试输出结果日志]
不推荐原因说明:转成html文件占用空间大,下载效率慢,建议下载jtl文件在本机直接,本处仅供了解。
例:jmeter -n -t /opt/testplanHn/count.jmx -r -l /opt/testplanHn/result_count_8.jtl -e -o /opt/testplanHn/result_count_8/ -j /opt/testplanHn/result_count_8.log
(推荐)下载jtl文件到本机windows电脑上,将jtl文件转成html报告命令:
jmeter -g [jtl结果路径] -o [结果文件html格式存放路径] ,注意:html存放文件夹需为空或不存在,否则生成会失败,失败信息:folder is not empty
例:jmeter -g F:\perf200218\result_login_500\resultCq_login_500_01.jtl -o F:\perf200218\result_login_500\html
2)解读命令行参数
-g : 指定已存在的测试结果文件
-n :以非GUI形式运行Jmeter,表示 non gui mode,就是非图形化模式
-t :source.jmx 脚本路径,
即testplan,后跟要运行的jmeter脚本的路径和脚本名称。
若指定路径下没有指定名称的脚本,则自动创建。
若没有路径只写脚本名称,则默认是在当前目录查找或创建。
-r:启动所有slave机执行,远程将所有slave机启动用在分布式测试场景下,不是分布式测试只是单点就不需要-r
-R:指定远程连接的slave机器的ip地址,多个ip之间用逗号隔开
-l :result.jtl 运行结果保存路径(.jtl),此文件必须不存在,重新执行需要手动删除已经生成的jtl文件
-j:存放日志路径。注意:不要写太多日志,否则转成html报告时会出现内存溢出错误。分布式时,该处是控制机日志,非执行机日志,执行机日志放置路径是各个执行机的/root/jmeter-server.log。脚本中输出的日志都是在执行机上。但是当输出日志时过大时转成html报告会报错Java Heap space,如下图所示:
-e :在脚本运行结束后生成html报告
-o :用于存放html报告的目录,指定的文件及文件夹,必须不存在 ,否则执行会失败。
注意:-r/R启动远程服务,即分布式执行启动slave机时,首先在这些slave执行机上启动jmeter-server的服务
3)分布式执行,启动成功标志如下:
五、Jmeter分布式执行注意点
单个压测机线程数配置<=1500,太多性能损耗较大,延时高。
1、分布式线程数设置
分布式运行时,总并发数是脚本中设置的线程数 * slave机的个数,如线程数设置为4,使用2个slave机运行,则总并发数是4*2=8.
例:执行1000并发时,脚本中设置的线程数=并发数/slave机的个数即1000/2=500。
分布式执行时,可通过最后一条summary=的Finished值进行判断本次执行的并发数是多少。
例:脚本设置线程数4,代理机2台,分布式执行时,该处的Finished是8,即进行的并发数是8
summary=为总的数据,368是请求数,23.9/s是TPS,Avg是平均响应时间单位ms,这里是0.319s,Err是错误率
其中主要有两种信息:summary + 和summary =,其它项都是类似的
summary + 203 in 00:00:15:在30秒内增加了203个请求,其中时间间隔由配置文件中的interval统计频率的值决定
summary = 368 in 00:00:15:在15秒内产生的总请求数是3685个,其中的时间段是从脚本运行开始计算到当前时间为止,一般在脚本运行过程中主要关注summary=信息即可
23.9/s:系统每秒处理的请求数,相当于TPS
Avg: 319:平均响应时间(单位ms)
Min: 178:最小响应时间(单位ms)
Max: 1761:最大响应时间(单位ms)
Err: 0 (0.00%):错误数/率
Active: 1:活动的线程数
2、Jmeter传参命令在分布式时不起作用(Jmeter缺陷)
Jmeter单机可使用传参命令动态修改线程数,但在分布式时不起作用,此问题是Jmeter存在的缺陷。故分布式时,只能通过本机修改线程数后再上传执行。
Jmeter单机传参命令参考如下(脚本中需设置对应的线程参数):
jmeter -Jthreads1=4 -Jrampup=1 -Jduration=20 -n -t /opt/testplanHn/count.jmx -l /opt/testplanHn/result_count_new4.jtl -j /opt/testplanHn/result_count_new4.log
3、分布式的多台单点替代方法
当分布式部署存在问题无法解决或者不想使用分布式时,可采取多台单点执行替代方法,统计数据时,并发数=单点数量并发数相加,TPS=多台单点的TPS相加,平均响应时间=多台单点平均响应时间相加/单点数量。
例:2台单点同时执行,并发数=2台单点并发数相加,TPS=2台单点的TPS相加,平均响应时间=2台单点平均响应时间的平均值。
4、单点命令行执行,保存响应结果(该方法只适应单点,且转成html报告存在问题,此处仅供了解,后续有新方法再更新。目前分布式的保存响应结果还待研究。)
Jmeter为了减少压力机负担,默认是不保存响应结果。需要做以下设置:
修改bin目录下的user.properties文件,追加配置:
jmeter.save.saveservice.output_format=xml
jmeter.save.saveservice.response_data=true
jmeter.save.saveservice.samplerData=true
jmeter.save.saveservice.requestHeaders=true
jmeter.save.saveservice.url=true
jmeter.save.saveservice.responseHeaders=true
但是该配置添加后,将jtl转化成html格式报告出错(原因:转换成html格式需要jmeter.save.saveservice.output_format=csv,linux环境默认是该配置),出错信息如下:
Error in NonGUIDriver java.lang.IllegalArgumentException: Report generation requires csv output format, check 'jmeter.save.saveservice.output_format' property
An error occurred: Error in NonGUIDriver Report generation requires csv output format, check 'jmeter.save.saveservice.output_format' property
所以以上保存错误信息的方法不可行。
通过以上设置生成的jtl文件,在windows查看会出现乱码,需要修改本机windows机器Jmeter配置:修改jmeter.properties 中的sampleresult.default.encoding=UTF-8
5、分布式保存错误响应结果
使用BeanShell Assertion元件进行断言操作。注意:不可写太多日志内容,以免影响性能,及转换成html时出现内存报错。
例:
String response = "";
String Str = "JMeter2";
String response = "${title}";
//response = prev.getResponseDataAsString();//获取当前请求响应结果
if(response == "")
{
Failure = true;
FailureMessage = "系统无响应,获取不到响应数据";
log.info(FailurMessage);
//}else if(response.contains(Str)==false){
}else if(response.equals(Str)==false){
Failure = true;
String msg = "\n系统返回响应结果与预期结果不一致!请排查是性能问题,还是程序代码问题";
FailureMessage = msg + "\n" + "期望结果:\n" + Str + "\n" + "响应内容:\n" + "${title}" + "\n";
log.info(FailureMessage);
}
分布式时,.log日志不存在控制机,而是执行机jmeter-server.log(默认在用户目录下如root目录)中,但是分布式的jtl文件转成html报告是可以看到报错日志。
执行机中日志显示如下所示:
控制机.jtl文件转换成html时响应结果显示Unicode编码,可通过网站http://tool.chinaz.com/tools/unicode.aspx 进行转码:
附录
1、windows版本jmeter内存修改
1)修改jmeter.bat文件,搜索HEAP
2)验证内存修改是否生效
运行文件 D:\Program Files\Java\jdk1.8.0_101\bin\jvisualvm.exe,查看本地Jmeter的信息,如下所示: