测试任务:测试bbs注册接口的tps(已经安装在虚拟机中),要求使用no-gui+分布式测试的形式;测试过程及问题记录如下
说明:
- master:控制机器,本次为我的win主机
- slave:负载机,虚拟机(linux)及本机(win)
- master ip:192.168.0.103
- slave ip:192.168.0.106
分布式测试
前提条件:
- win和虚拟机都安装好了相同版本的jmeter和jdk
- bbs的注册脚本已经调试好,能够成功注册
注:这里只记录了正常情况,如果遇到异常,请直接参考本文末尾的问题列表
1、在salve中启动server: jmeter-server(配置环境变量即可在任意目录启动)
2、在master的配置文件jmeter.properites中修改remote_hosts,添加虚拟机ip
我把本机ip也顺便添加进去了,即本机既做master又做slave
3、启动master的jmeter(先保证分布式可以调通,再考虑no-gui)
执行run>remote start>192.168.0.106
可以看到已经执行成功,slave上会记录执行过程,master会得到执行结果
slave记录执行过程:
master得到执行结果
如果这一步不能成功,请查看相关日志报错信息,再问度娘寻求帮助,或者参考文末我遇到的问题及解决办法。
注:此处的日志指的是slave上的server日志(日志位置存放在jmeter-server的启动目录)或者master上的日志(启动jmeter.bat的目录)
分布式+no-GUI调试
脚本优化
查看官网的提示:
以上信息大概是说要尽量精简脚本,避免带来不必要的性能损耗,主要手段:
- 尽可能少的使用监听器
- 不要使用’View Results Tree’ 和’View Results in Table’,这两个只是我们调试脚本用的
- Don’t use functional mode,此处暂不太明白,是指函数助手吗?
- 尽可能少的使用断言
- 如果脚本需要大量的随机数据,请使用csv data来参数化(此处没有建议使用函数助手,所以第三点才那样猜测的)
根据以上原则,我的脚本大概如下:
即:使用csv data参数化注册需要的用户名,不使用断言。那么怎么知道程序是否运行正常呢?因为是注册,所以统计执行前后数据库中的用户数量变化即可知道是否注册成功。
执行脚本之前数据库中的用户:
故意删除了其他数据,使执行前只有一条记录
执行脚本
先计划执行2个线程,每个线程执行10次,查看效果(注:当前设置会导致脚本运行异常,后面有解决办法)。如果采用两台slave,每台slave独立执行整个测试计划,所以预期的总请求数应该为:2个请求 * 2个线程 * 10次 * 2台slave=80
本次把本地机器和虚拟机都作为slave,因此先把本地机器和虚拟机器的server开启:特别注意linux执行目录下需要含有csv data用到的参数化文件,win中需要将参数化文件放到jmeter-server的同级目录
win执行jmeter-server:
虚拟机中,在含有userinfo.txt的目录下执行jmeter-server
将win同时作为master控制slave的执行,执行以下命令:
jmeter -n -t xxx.mx -r -l result.jtl -e -o output
或者
jmeter -n -t xxx.mx -r -l result.jtl
jmeter -g result.jtl -o output
运行完成后,发现请求个数只有40个,数据库中只有两条记录,而且都是虚拟机中运行的结果,本机没有运行。
数据库没有数据:
观察数据库,请求全部是是虚拟机上发出来的,且只有2个成功(也就是每个线程成功1次),经过排查发现是cookie管理器的问题,如果每次迭代不清空cookie管理器,无法注册(业务上解释就是登陆用户不允许注册),清空cookie管理器即可。
本地机器没有运行:
经过日志排查,发现需要将userinfo.txt放到jmeter-server的同级目录(linux中只需要放到jmeter-server的启动目录即可);
由于每个slave单独运行整个线程组,所以这里的csv文件在每个slave上不能相同(看上去不太科学~,但是目前的测试结果只能得出这样的结论,如果win和linux使用相同的csv失败率会是50%),否则会造成注册名冲突而注册失败。
再次验证
- 重新生成一份userinfo.txt放到linux上(保证数据和win不相同)
- 将win上的userinfo.txt放置到jmeter-server脚本的同级目录(如:E:\apache-jmeter-5.1.1\bin)
- 再次清空数据库(因为之前的用户名注册了,现在不想修改userinfo.txt,所以清库),并验证结果:
请求总数为80,数据库新增条数为40,且含有两个slave的数据,说明调试成功
正式运行脚本
设置合适的线程数:暂时设置为 50个线程运行15分钟
- 开启相应的jmeter-server服务
- 清空数据库user表
- 清空之前的所有结果数据:result.jtl,jmeter.log以及output文件夹
- 关闭slave上不必要的程序
- master控制所有slave运行起来:jmeter -n -t xxx.mx -r -l result.jtl -e -o output
测试结果
确实只运行了15分钟
该场景下(也就是50 * 2=100个线程运行15分钟)的测试结果:注册请求的tps为:7.60 Transactions/s(可以看到rt偏大,平均值需要7s,看上去不太科学,应该要缩小线程数,具体应该设置多少线程才能得到最佳tps?据说是要找到系统拐点,也就是随着线程的增大tps不再增大或减小的线程数,为最佳线程数。暂时未找到寻求拐点的有效测试方法)
数据库条数新增了6897条(运行前数据库有一条记录),正确率为99.99%:
最终的文件及文件说明
bbs_register.jmx: 注册脚本文件,含两个请求,进入注册页面及注册请求(因为必须进入注册页面获取formhash才能注册,所以测试出来的注册请求的tps应该比实际偏小)
userinfo.txt: 文件为注册用的用户名数据(10万条,python脚本自动生成)
userinfo2.txt: linux所用的参数化文件(上传到虚拟机后需要修改名字为userinfo.txt)
result.jtl: 测试结果文件,由非GUI模式命令生成:jmeter -n -t bbs_register.jmx -l result.jtl
jmeter.log: 非GUI模式下的日志文件
output文件夹: 由result.jtl生成的jmeter html报告:jmeter -g result.jtl -o output
generate_user.py:python脚本,用于生成10万条随机数据(或许根本用不到10万条,具体该生成多少条不太清楚计算规则)
遇到的其他异常:
1.slaves启动jmeter-server服务报错Cannot start. besttest is a loopback address
修改host配置,将besttest映射为虚拟机ip
2.master执行remote start报错:java.io.FileNotFoundException: rmi_keystore.jks
需要在master的jmeter.properties中设置:server.rmi.ssl.disable=true
3.master执行remote start报错:RMI non-JRMP server at remote endpoint:
需要在slave的jmeter.properties中设置:server.rmi.ssl.disable=true
4.调用远程任务后,客户端查看一直处于启动中,服务端日志报错:java.rmi.ConnectException: Connection refused to host:xxxx;
4.1 slave的jmeter-server添加RMI_HOST_DEF为虚拟机ip:
vi jmeter-server
RMI_HOST_DEF=-Djava.rmi.server.hostname=192.168.0.106
4.2 master修改jmeter.bat:
set rmi_host=-Djava.rmi.server.hostname=192.168.0.103
set ARGS=%JAVA9_OPTS% %DUMP% %HEAP% %VERBOSE_GC% %GC_ALGO% %DDRAW% %SYSTEM_PROPS% %JMETER_LANGUAGE% %RUN_IN_DOCKER% %rmi_host%
5.测试结果明显不对,执行时间不是15分钟:
将linux时间和本地机器时间同步即可;
yum -install ntpdate
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
ntpdate -u ntp.api.bz