高并发压力测试用JMeter负载机自身的短板往往比被测系统更早出现。一旦负载机资源耗尽,就会出现 TPS 上不去、响应时间异常波动、甚至报错等问题。
一、短板速查
在非GUI方式下启动测试后,如果发现这些现象,说明负载机可能已成短板:
增加线程数后,吞吐量不再上升甚至下降
jmeter.log 中出现 OutOfMemoryError 或 GC 频繁警告
CPU 使用率长时间 100%(尤其是单核),系统负载飙升
大量请求报 connection timeout 或 no buffer space available
网络流量远未达到带宽上限,但请求卡住
二、短板定位
1. 系统资源监控
在JMeter非GUI运行的同时,另开终端不断采集数据:
整体监控(推荐 sar 历史记录)
bash
# 每隔2秒记录一次,保存到文件
sar -A -o /tmp/sar_report 2 0 > /dev/null &
CPU 和内存
bash
top -b -d 5 -n 10 | tee top.log # 注意 us,sy,id,wa
vmstat 2 10 # 看 r 列(运行队列)、si/so
网络连接和流量
bash
sar -n DEV 2 # 网卡流量是不是接近带宽上限
ss -s # 连接总数及状态分布
watch -n 1 'ss -tan state time-wait | wc -l' # TIME_WAIT 数量
文件描述符使用
bash
ls /proc/<jmeter_pid>/fd | wc -l # JMeter 占用的 fd 数
磁盘 I/O(如果结果文件写入量巨大)
bash
iostat -x 2
2. JVM方面监控
找到JMeter进程PID,使用JDK自带工具:
bash
# 查看 GC 频率和耗时,每1秒打印一次
jstat -gcutil <pid> 1000
# 如果 Full GC 频繁,需调整堆或优化脚本
# 也可在启动时加 GC 日志参数(见下文)
推荐启动时直接开启GC日志,方便事后分析:
bash
# 在 jmeter 启动脚本中设置 JVM 参数
JVM_ARGS="-Xlog:gc*:file=/tmp/gc.log:time,uptime:filecount=5,filesize=10M"
3. JMeter 自身日志
实时观察 jmeter.log,检查是不是有错误或资源不足的异常:
bash
tail -f jmeter.log | grep -E "ERROR|OutOfMemory|SocketException"
三、常见短板及优化实战
1. JVM 调优 - 最直接有效
默认的 JMeter 堆内存一般只有 512MB,高并发下远不够。
修改 jmeter 启动脚本(bin/jmeter)或通过环境变量包括:
bash
export HEAP="-Xms4g -Xmx4g -Xss256k -XX:MaxMetaspaceSize=256m"
使用更适合低延迟的垃圾收集器:
bash
export JVM_ARGS="-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:G1ReservePercent=20"
注意:堆并不是越大越好,超过物理内存会导致 swap,性能急剧恶化。建议不超过物理内存的 70%,预留空间给 OS 缓存和 socket 缓冲区。
2. 测试计划脚本优化
非GUI运行时,脚本中的低效组件会被成百上千线程放大。
禁用所有图形监听器:如查看结果树、聚合报告等,它们会消耗大量内存和 CPU。仅保留简单数据写入器或 -l 参数输出 CSV。
替换BeanShell:永不要在循环中使用 BeanShell,换成 JSR223 Sampler/PreProcessor + Groovy,并勾选 Cache compiled script。
精简断言:减少正则表达式断言,改用边界提取器 + 简单比较。多断言可考虑 jp@gc - Dummy Sampler 思路处理。
CSV Data Set Config 配置:
设置 Recycle on EOF = False、Stop thread on EOF = True,避免线程因等待数据而空转。
尽量将变量读取方式设为 Sharing mode: All threads,节省内存。
HTTP 请求优化:
实现选择 HTTPClient4,连接池复用:在 user.properties 中设 httpclient4.validate_after_inactivity=3000,httpclient4.time_to_live=60000。
统一设置超时:httpclient.timeout=30000。
如果被测系统支持,开启 keep-alive(Client 实现自动维护)。
3. 操作系统参数调优
解决端口耗尽、连接堆积等问题。
bash
# 查看当前限制
ulimit -n
# 临时加大(建议至少 65535)
ulimit -n 65535
永久生效需修改 /etc/security/limits.conf:
text
* soft nofile 65535
* hard nofile 65535
内核网络参数(/etc/sysctl.conf 或 /etc/sysctl.d/99-jmeter.conf):
ini
# 端口范围扩容
net.ipv4.ip_local_port_range = 1024 65000
# 加速 TIME_WAIT 回收(负载机作为客户端时可开启,不会造成连接串扰)
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
# 增加全连接队列长度
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 8192
执行 sysctl -p 使之生效。
4. 结果输出和磁盘I/O优化
非GUI方式下,通过-l 指定结果文件,只输出CSV格式,不要实时生成HTML报告(报告事后用 -g 生成)。
bash
jmeter -n -t test.jmx -l result.jtl
在高并发时写入详细结果可能成为短板,可修改 jmeter.properties 或通过命令行参数只保留必要字段:
properties
jmeter.save.saveservice.output_format=csv
jmeter.save.saveservice.bytes=false
jmeter.save.saveservice.sent_bytes=false
jmeter.save.saveservice.thread_name=false
jmeter.save.saveservice.latency=true
jmeter.save.saveservice.connect_time=true
如果结果量实在太大,可开启批量写入方式(简单数据写入器中设置 interval 为非 0 值),降低IO频率。
5. 分布式横向扩展
当单台负载机经过所有优化后仍达到硬件极限(CPU/网络/内存),就需要做分布式。
在负载机上启动 JMeter Server(非 GUI):
bash
jmeter-server -Djava.rmi.server.hostname=<本机IP>
控制机执行(也是非 GUI):
bash
jmeter -n -t test.jmx -R node1_ip,node2_ip -l result.jtl
结果文件的合并和报告生成可随后进行。
四、实战定位流程
假设你要压测5000并发,启动负载机监控脚本,再执行:
bash
nohup jmeter -n -t test.jmx -l /dev/null -e -o /dev/null &
用 top 观察到 CPU %us 接近 99%,同时 jstat -gcutil 看到 FGC 每秒多次,内存接近堆上限 - 决定为 JVM 堆过小+GC 压力。
调整堆为 8G,增加 G1GC,重新执行,CPU 降到 80%,TPS 提升。再次监控发现网络流量打满 1Gbps → 网络成为短板,此时应增加负载机分布式压测。
五、检查清单
使用最新稳定版 JMeter(5.5+)
HEAP内存已设为 2G 以上,并开启 GC 日志
脚本中移除了所有图形监听器
避免使用 BeanShell,采用 JSR223+Groovy
操作系统 ulimit -n ≥ 65535,TCP 参数已优化
HTTP 请求启用连接复用和合理超时
结果仅输出 CSV,且监控磁盘 I/O 未满
当单机短板不可解时,已准备分布式环境
定位负载机短板的重要思路就是 监控-定位-分层优化,在非GUI下依靠命令行工具和JMeter自身日志即可完成绝大多部分分析。