先说我踩过的坑:平均响应时间把我坑惨了
大概是 2021 年,我帮一个电商项目做压测,环境是预发,压测机 4C8G,JMeter 5.4.1,GUI 模式,200 线程,Ramp-up 60 秒,循环 50 次,测的是下单接口。 结果平均响应 120ms,p95 400ms,错误率 0,报告看起来挺漂亮,我还截图发群里了。 上线大促当天,p99 直接 3.8 秒,超时率 7%,客服群里炸了。 后来复盘发现,压测机 CPU 跑到 90%,JMeter GUI 本身吃掉不少资源;数据库连接池 maxActive=8,应用线程池 core=20 max=20,JVM 只给了 -Xmx2g,Full GC 一次 1.8 秒。 把连接池改到 50,线程池 max=200,JVM 改成 -Xms4g -Xmx4g + G1,p95 从 2.1 秒降到 380ms。 这事让我明白:压测报告好看,不代表系统能扛,工具、环境、参数、监控,缺一个都可能翻车。
工具对比:别问哪个最好,先问你在哪跑、谁来维护
我按 4C8G 云主机、内网、简单 GET、返回 200B 的脚本做过一轮参考测试,数字不是标准答案,但能看出脾气。
| 工具 | 语言/并发模型 | 4C8G 简单 HTTP 参考 | 适合场景 | 我遇到的坑 |
|---|---|---|---|---|
| JMeter | Java 线程 | 2k-6k RPS | 复杂协议、业务流、插件多 | GUI 压测吃资源,分布式配置烦 |
| k6 | Go goroutine | 5k-15k RPS | CI 回归、脚本即代码 | 协议支持不如 JMeter,报告要接 Grafana |
| Locust | Python 协程 | FastHttpUser 3k-8k,普通 1k-3k | Python 团队、自定义逻辑 | 普通 HttpUser 性能一般,GIL 要注意 |
| Gatling | Scala/Akka | 5k-10k RPS | 高并发、报告漂亮 | Scala 学习曲线,排错不直观 |
| wrk | C/epoll | 20k-50k RPS | 裸 HTTP 基准 | 不适合业务流、参数化弱 |
JMeter 我通常这样跑非 GUI:jmeter -n -t api.jmx -l result.jtl -e -o report,堆改成 -Xms4g -Xmx4g,summariser.interval=30。 k6 的脚本长这样:
export const options = {
stages: [
{ duration: '30s', target: 50 },
{ duration: '1m', target: 200 },
{ duration: '2m', target: 200 },
{ duration: '30s', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<500'],
http_req_failed: ['rate<0.001'],
},
};
Locust 常用:locust --headless --users 1000 --spawn-rate 50 --run-time 10m --csv=result。 Gatling 的 injection:rampUsers(1000) during(60 seconds),constantUsersPerSec(200) during(10 minutes)。 wrk 裸测:wrk -t12 -c400 -d30s --latency。 选型我的土办法:CI 回归用 k6,复杂协议用 JMeter,Python 团队用 Locust,高并发报告用 Gatling,裸 HTTP 用 wrk。
我的独立观点:性能测试不是压出高并发,而是建容量模型
很多人一上来就问能支持多少并发,这个问题其实不完整,应该问在 p95 小于多少、错误率小于多少的前提下,能支持多少 RPS。 用 Little's Law 估算:并发 = RPS × 平均响应时间。目标 3000 QPS,平均 150ms,那并发约 450;如果 p95 300ms,p99 800ms,要留 30%-50% 余量,VU 设 600-800 更稳。 我现在的做法是先定性能预算:p95<500ms,p99<1s,错误率<0.1%,CPU<70%,GC pause<200ms,数据库连接池活跃<80%。 然后按 10%、25%、50%、75%、100%、120% 做梯度,每阶段 5-10 分钟,找 RPS 不涨、响应时间线性涨、错误率冒头的拐点。 不要只看平均值,平均值会把长尾吃掉;不要只压单接口,链路里的连接池、缓存、DB 锁才是戏肉。 监控至少上 Prometheus + Grafana,node_exporter 看机器,mysqld_exporter 看 DB,jmx_exporter 看 JVM。 没有监控的压测,就是蒙眼开车。
具体步骤:从目标到回归
- 估目标:日请求 2000 万,80% 集中在 9 小时,峰值因子 3,峰值 QPS = 2000万0.8/(93600)*3 ≈ 1481,再留 2 倍余量,目标 3000 QPS。
- 建环境:预发同规格,数据量生产 30%-50%,缓存预热 10 分钟,关闭 debug 日志,压测机和被测服务分开。
- 写脚本:参数化手机号、用户 ID,关联 token,加断言,思考时间 1-3 秒,集合点看场景,不要所有接口都无脑并发。
- 执行:先单接口,再核心链路,再全链路;先基准,再梯度,再稳定性,最后破坏性。
- 监控:top、pidstat -u 1、iostat -x 1、vmstat 1、jstat -gcutil、arthas dashboard、慢查询日志。
- 调优:连接池 maxActive=50,线程池 max=200,JVM -Xms4g -Xmx4g + G1,热点缓存,DB 索引,网关限流。
- 回归:CI 跑 k6 50 VU 5 分钟,thresholds p95<500ms,错误率<0.1%,不达标直接 fail,别等上线。
避坑清单
- JMeter GUI 压测,压测机先跪。
- 只看平均响应时间,不看 p95、p99。
- 没有思考时间,把系统压成理想状态。
- 压测机网卡、CPU、内存成瓶颈。
- 测试数据只有几百条,缓存全命中。
- 没有监控,出问题靠猜。
- 只压一个接口,忽略链路。
- 忽略 GC、连接池、线程池、DB 锁。
- 没有基线,每次报告都没法对比。
- 把一次压测报告当永久结论。
结论
工具没有绝对好坏:k6 适合 CI 和脚本化回归,JMeter 适合复杂协议和业务流,Locust 适合 Python 团队,Gatling 适合高并发和漂亮报告,wrk 适合裸 HTTP 基准。 真正的深度不是把并发数堆到 10 万,而是知道系统在什么业务量、什么数据量、什么阈值下会拐。 性能测试很枯燥,调参、等结果、看监控,但找到瓶颈那一刻确实很爽。 别迷信工具,别只盯着并发数,先把容量模型和基线治理做起来。