目录

测试资源

  • 1台被压测机器
    • centos版本信息参考线上服务器
    • 双核4G
    • 业务服务部署:ws服务(golang实现websocket的一个服务)、redis服务(本地6379端口)
    • 监测服务:prometheus、grafana、cacti。需要监测服务器资源使用情况、需要监测go服务使用情况
    • 系统配置调整:ulimit
  • 2台压力机器
    • 双核2G内存
    • ubuntu18及以上。
    • 服务部署:最新jdk、jmeter服务安装与配置(slave)
  • 1台压力控制机
    • 单核1G内存,连接操作顺畅即可
    • 服务部署:最新jdk、jmeter服务安装与配置(master)
  • 其他
    • 竞价实例
    • 4台机器在同一个局域网内
    • 关闭防火墙

测试环境准备

系统配置调整

  • ulimit调整系统文件

    临时调整了ulimit为50000,默认是1024,会影响数据库、redis等服务连接数

    修改/etc/security/limits.conf文件,在文件中添加如下行,修改系统对所有用户的软限制(软限制是指限制用户同时打开的文件数目,硬限制是指系统根据硬件资源(主要指内存)计算出来的最多可打开的文件数目)

    * soft nofile 60000
    * hard nofile 60000
    

redis

yum install redis

定期删除redis所有数据:

flushall

定制ws服务

  • 调整token机制,避免泄露
  • 增加控制端与被控制端连接统计,提供给prometheus采集

分支:jm-loadtest

配置文件:config.cfg(参考测试环境)

jmeter服务

  • jdk

    ubuntu快速安装配置jdk

    master控制机、slave压力机需要使用相同版本的jdk。

  • jmeter

    master控制机、slave压力机安装相同版本的jmeter

    • 下载

      Apache JMeter - Download Apache JMeter

    • 解压到指定位置

      比如unix可以尝试放在/usr/local/jmeter下

      windows环境,解压可以执行GUI界面即可。

    • unix环境配置全局环境变量

      vim /etc/profile

      export JMETER=/usr/local/jmeter
      export PATH=$JMETER/bin:$PATH
      
      source /etc/profile
      
  • 配置master控制机

    修改master控制机bin/jmeter.properties配置,添加需要控制的远程压力机:

    remote_hosts=192.168.232.128:1099
    

    不启用ssl:

    server.rmi.ssl.disable=true
    

    修改bin/system.properties配置,用于告知slave回传数据给master的地址:

    java.rmi.server.hostname=192.168.232.131
    
  • 配置slave压力机

    用于与master的数据传输,比如master发送脚本到slave,salve发送结果给master,修改bin/jmeter.properties配置:

    # RMI port to be used by the server (must start rmiregistry with same port)
    server_port=1099
    
    

    不启用ssl

    server.rmi.ssl.disable=true
    

测试数据

  • 10万个phone用户数据
  • 20万个web用户数据,2个web用户与1个phone用户关联,也就是1个控制端对应2个被控制端

暂时提供csv的参数化方式,没有使用JDBC的方式直接数据库。需要将参数配置数据同时上传到压力机jmeter-server启动的目录下(注意相对路径)

附:测试数据生成存储过程

use jmtest;
-- 删除过程
DROP PROCEDURE test_web_insert;

-- 定义存储过程
DELIMITER $$
CREATE PROCEDURE test_web_insert(max_num INT)
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE j INT DEFAULT 0;
DECLARE _phone_id VARCHAR(32) DEFAULT '';
DECLARE _web_id VARCHAR(32) DEFAULT '';
DECLARE _token VARCHAR(32) DEFAULT '';
DECLARE _instance_id VARCHAR(15) DEFAULT ''; 
SET autocommit = 0;
REPEAT
SET _phone_id = MD5(UUID_SHORT());
SET _instance_id = concat(substring(_phone_id,1,7),"_",substring(_phone_id,10,7));
SET _token = MD5(CONCAT(MD5(CONCAT(_phone_id,"9ong")),"loadtest"));    
SET j=0;
SET i = i + 1;
INSERT INTO `phone_data`(phone_device_id,token,instance_id) VALUES(_phone_id,_token,_instance_id);
WHILE j<2 DO
SET _web_id = MD5(UUID_SHORT()); 
INSERT INTO `web_data`(phone_device_id,web_device_id,token,instance_id) VALUES(_phone_id,_web_id,_token,_instance_id);
SET j=j+1;
END WHILE;
UNTIL i = max_num
END REPEAT;
COMMIT;
END $$

-- 删除
DELIMITER ;

--- 调用过程
CALL test_web_insert(2);


-- 重置表
TRUNCATE TABLE jmtest.`web_data`;
TRUNCATE TABLE jmtest.`phone_data`;

服务启动

  • 启动ws服务

    ./wssrv >>/var/log/wssrv.log &
    
  • 启动压力机jmeter-server服务

    //hostname为本机ip
    jmeter-server -Djava.rmi.server.hostname=192.168.232.xxx
    
  • jmeter控制机压测

    注意:需要先启动压力机jmeter-server后,开始压测时才执行

    //linux
    jmeter -n -t ws-plan.jmx -r -l ws-plan.jtl
    
    //windows
    .\jmeter.bat -n -t E:\jmeter\ws-phones-connect.jmx -r -l E:\jmeter\jtl\ws-phones-connect-1659.jtl
    
    .\jmeter.bat -n -t E:\jmeter\ws-web-phone.jmx -r -l E:\jmeter\jtl\ws-web-phone-1535.jtl
    

    注意:jmeter如果没有配置全局环境的话,要在jmeter/bin目录下执行命令行

测试计划

小数据场景

假设:总控制端数M,总被控制端数N(N是M的2倍)

  • 记录服务器初始化状态

  • 并发连接

    • 1、被控端并发连接(ws-phones-connect)

      脚本:1秒内1000并发,各发一次ping,1至5s后关闭

    • 2、被控制端并发连接,保持连接,即维持心跳一段时间(ws-phones-connect-ping)

      在步骤1的基础上出现连接失败临界并发点时,再考虑最大可成功连接的并发数下,维持心跳10次(甚至更多),每次10至30s随机发送ping,之后1至5s随机关闭连接

    • 3、被控端与控制端混合并发连接

      直接并发

    • 4、被控端与控制端混合并发,并保持连接

      在步骤3的基础上找到临界并发点,参考2

    • 5、模拟远程控制场景(ws-web-phone)

      控制端与被控端一对并行并发连接,模拟远程控制,10至30s随机发ping,10至30次后,控制端在随机ping中穿插转发控制消息,在ping周期结束1至5s之后关闭连接

  • 保持连接(ws-web-phone-most)

    阶梯性连接(5000web+5000phone)/10s,直到连接大量失败,找到可保持的连接数临界点。维持心跳(10至30s) * 60,控制端 40% * 60 次概率性转发消息,每个连接在60次心跳之后1至5s正常关闭连接

  • 并发连接异常断开(ws-web-phone-most)

    在保持连接压测的基础上,压测时,主动关闭压测,即异常中断所有连接,观察后续服务器状态,有没有恢复到压测前的状态

  • 并发非法重连。模拟客户端使用旧连接重试处理不当时,导致疯狂重连

  • 并发关闭连接

压测前状态恢复

  • 等服务器负载稳定
  • 小数据服务重启保证初始化状态一致
  • 清空redis

操作步骤

  • 执行jmx,注意jtl文件命名
  • 观察gfafana
  • excel记录开始和结束时间
  • 导出jtl,导入jmeter GUI查看
  • grafana截图最近一段时间曲线图
  • 等待服务恢复

跟踪测试问题

1、redis 连接too many open file

临时调整了ulimit为10000,默认是1024,会影响数据库、redis等服务连接数

修改/etc/security/limits.conf文件,在文件中添加如下行,修改系统对所有用户的软限制(软限制是指限制用户同时打开的文件数目,硬限制是指系统根据硬件资源(主要指内存)计算出来的最多可打开的文件数目)

* soft nofile 60000
* hard nofile 60000

2、本次虚拟机初步压测

两台虚拟机作为压测机,宿主机windows作为控制机,组成一个局域网。

另外其中一台虚拟机提供小数据服务。

虚拟机配置:4G内存、双核

初步压测被控端并发连接,并发策略是两台机器分别同时并发10000个用户(thread),发起小数据服务连接,并在连接成功后10s,发送一次ping,再等5s主动关闭连接,初步得到一个大概的数据:

```
label   samples average median  90%line 95%line 99%line min Max Error%  Throughput Received Sent
new connection	20000	2990	19	6430	22159	31231	0	32106	0.05465	211.8846076426778	44.23314656588022	1.1930303537943237
ping and pong	20000	294	0	0	1	19834	0	25061	0.04095	1.2234507086371877E-5	1.3031590002153336E-6	8.03684053638119E-8
close connection	20000	3527	1828	8270	11247	21851	0	24029	0.30585	1.2234507048936118E-5	3.3174152609449244E-8	3.5591722266497074E-7
close connection-0	14866	953	0	2861	3771	7555	0	19605	0.0	160.38753668220266	17.848036726706802	0.0
close connection-1	180	0	0	0	0	0	0	20	0.0	3.274989993086132	0.36459849532404204	0.0
close connection-2	38	1	0	0	0	43	0	43	0.0	21.952628538417102	2.443944974003466	0.0
TOTAL	75084	2003	1	6322	8688	29668	0	32106	0.10693356773746737	4.593078636311597E-5	4.9172383523639865E-6	5.051728321696359E-7
```

(可读性强点可以再导入压测结果文件jtl:jmeter\jtl\ws-phones-connect-1659.jtl)

3、并发10000连接时出现WebSocket I/O error: Read timed out

Thread Name:172.16.0.16:1099-phone threads group 1-1002
Sample Start:2021-10-25 13:32:54 CST
Load time:6004
Connect Time:0
Latency:0
Size in bytes:0
Sent bytes:0
Headers size in bytes:0
Body size in bytes:0
Sample Count:1
Error Count:1
Data type ("text"|"bin"|""):
Response code:Websocket I/O error
Response message:WebSocket I/O error: Read timed out


SampleResult fields:
ContentType: 
DataEncoding: null

之前默认连接时,如果超过6000ms没有响应就认为连接失败,暂时调整为10000ms,当然在被压测云服务器这么快就发生了这个处理不过来的连接响应,有点意外,本地虚拟机在10000并发时,还是很顺畅的。

解决:

结果发现:是因为并发地址是被压机器的外网ip,可能因为云外网环境线路等原因导致这个请求链较长

【jmeter知识】03_jmeter接口报Read timed out +ng日志499分析 - chooperman - 博客园

4、压力机并发限制,内存不足问题

在一次每台并发20000的场景时,压力机出现了错误,

Starting the test on host 172.16.0.16:1099 @ Mon Oct 25 16:05:22 CST 2021 (1635149122254)
[9495.706s][warning][os,thread] Failed to start thread - pthread_create failed (EAGAIN) for attributes: stacksize: 1024k, guardsize: 0k, detached.
Uncaught Exception java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached in thread Thread[StandardJMeterEngine,5,RMI Runtime]. See log file for details.

解决:

编辑jmeter.bat或jmeter(shell文件),调整最大可使用内存Xmx为2g,Xms表示初始化内存,由于压力机是4g内存,暂时考虑官方建议不超过物理内存的一半:

\# This is the base heap size -- you may increase or decrease it to fit your
\# system's memory availability:
\#: "${HEAP:="-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m"}"
: "${HEAP:="-Xms1g -Xmx2g -XX:MaxMetaspaceSize=256m"}"

以上方案失败了

解决2:

官方这么说的:

To set those variables permanently, you can place them in a file called setenv.sh in the bin directory. This file will be sourced when running JMeter by calling the jmeter script

所以我们新建文件jmeter/bin/setenv.sh:

export HEAP="-Xms2g -Xmx2g -XX:MaxMetaspaceSize=512m"

注意:setenc.sh文件,因为md文档原因,缺失了shell文件头,需要自己补充

Apache JMeter - User’s Manual: Getting Started

解决3:

由于已经不是堆的问题,超出RAM的问题,只能应加机器,或提升内存。

5、jtl文件太大,在linux上生成报告问题

目前最新的jdk17不支持jmeter在linux上生成报告。

需要降级到jdk16