性能基准测试:基准测试方法论、测试环境搭建、压测工具选型(wrk、JMeter、Locust)、测试脚本编写
性能基准测试,说白了就是给你的系统拍一张「体检照」。
我刚开始做报价路由系统时,总觉得上线后跑得挺快。直到有一次,某大客户投诉说「你们的报价响应时间怎么忽高忽低?」。我查了半天,发现是压测环境没搭好,数据全是假的。嗯,从那以后,我每次做性能调优,第一件事就是先把基准测试做扎实。
基准测试方法论:先定标准,再谈优化
基准测试不是随便跑几个请求就完事了。它有一套成熟的方法论,我习惯把它拆成三步:
- 定义关键指标:响应时间(RT)、吞吐量(TPS/QPS)、错误率、资源利用率(CPU、内存、IO)。
- 确定测试场景:单接口压测、混合场景压测、稳定性压测(长时间运行)。
- 建立基线:在相同环境下跑一次,记录数据作为后续优化的参照。
核心原则:基准测试的结果必须可复现。如果两次跑出来的数据差20%以上,说明你的测试环境或脚本有问题。
我在项目中遇到过最坑的事:开发环境用笔记本压测,结果CPU降频导致数据忽高忽低。后来我强制要求所有基准测试必须在专用压测机上跑,且压测机与被测服务之间不能有网络抖动。
测试环境搭建:隔离、隔离、再隔离
环境搭建这件事,我吃过不少亏。你想想看,如果压测时数据库还在跑定时任务,那数据能准吗?
我建议按以下标准来搭:
| 组件 | 推荐配置 | 避坑指南 |
|---|---|---|
| 压测机 | 4核8G以上,独立机器 | 不要用笔记本,不要与被压服务混部 |
| 被测服务 | 与生产环境同规格(至少同CPU架构) | 我曾经用低配机器压测,上线后直接被打爆 |
| 数据库/缓存 | 独立部署,关闭自动备份和定时任务 | 记得预热数据,避免冷启动影响结果 |
| 网络 | 千兆局域网,避免跨机房 | 网络延迟会污染你的RT数据 |
注意:压测前一定要检查系统资源限制。ulimit、文件句柄数、TCP连接数,这些默认值往往不够用。我曾经因为ulimit没改,压到一半连接全被拒绝了。
压测工具选型:wrk、JMeter、Locust
工具选型这件事,没有银弹。我根据不同的场景,会选不同的工具。
wrk:轻量级、高性能
wrk 是我个人最常用的工具。它基于 C 语言,单机就能压出很高的并发。适合快速验证接口性能。
优点:
- 资源占用极低,一台4核机器就能压出10万+ QPS
- 支持 Lua 脚本,可以自定义请求逻辑
- 输出简洁,适合自动化集成
缺点:
- 不支持分布式压测
- 不支持图形化界面
- 调试起来比较麻烦
JMeter:功能全面、适合复杂场景
JMeter 是 Java 生态的老牌工具。如果你需要做复杂的业务场景(比如登录-查询-下单-支付),JMeter 的图形化界面和丰富的插件会让你省很多事。
优点:
- 支持分布式压测
- 有丰富的断言和监听器
- 社区活跃,遇到问题容易找到答案
缺点:
- 资源占用高,单机压测能力有限
- 脚本维护成本高,尤其是参数化
Locust:Python 友好、适合持续集成
Locust 是用 Python 写的,如果你团队里都是 Python 开发者,那它很合适。它的脚本就是 Python 代码,可以很方便地集成到 CI/CD 流水线中。
优点:
- 脚本灵活,可以写复杂的业务逻辑
- 支持分布式,且扩展简单
- Web UI 实时查看结果
缺点:
- 性能不如 wrk,单机压测能力有限
- 对异步支持不太好
我的选择建议:
- 快速验证接口性能 → wrk
- 复杂业务场景压测 → JMeter
- 持续集成/自动化压测 → Locust
测试脚本编写:从入门到实战
脚本编写是基准测试中最容易出问题的地方。我见过太多人写出来的脚本根本压不到目标接口,或者压出来的数据全是错的。
wrk 脚本示例
先来个最简单的:
# 压测 GET 接口
wrk -t12 -c400 -d30s http://localhost:8080/api/quote
# 参数说明:
# -t12:12个线程
# -c400:400个并发连接
# -d30s:持续30秒
如果你需要自定义请求头或 body,可以用 Lua 脚本:
-- request.lua
wrk.method = "POST"
wrk.body = '{"symbol": "BTC/USDT", "side": "buy"}'
wrk.headers["Content-Type"] = "application/json"
-- 压测时引用脚本
wrk -t12 -c400 -d30s -s request.lua http://localhost:8080/api/quote
注意:wrk 的 Lua 脚本不支持复杂的逻辑。如果你需要做参数化(比如每次请求不同的 symbol),建议用 JMeter 或 Locust。
JMeter 脚本示例
JMeter 的脚本我习惯用 GUI 先配好,然后保存为 .jmx 文件。核心步骤:
- 添加「线程组」:设置并发数、Ramp-Up 时间、循环次数
- 添加「HTTP 请求默认值」:配置协议、域名、端口
- 添加「HTTP 请求」:配置具体接口路径、参数
- 添加「监听器」:聚合报告、响应时间图
如果你需要参数化,可以用 CSV 数据文件:
# symbols.csv
symbol
BTC/USDT
ETH/USDT
SOL/USDT
然后在 JMeter 中添加「CSV 数据文件配置」,引用这个文件。这样每次请求都会取不同的 symbol。
Locust 脚本示例
Locust 的脚本就是 Python 代码,非常直观:
from locust import HttpUser, task, between
class QuoteUser(HttpUser):
wait_time = between(1, 3) # 模拟用户思考时间
@task
def get_quote(self):
self.client.get("/api/quote?symbol=BTC/USDT")
@task(3) # 权重为3,表示这个接口被调用的概率更高
def post_order(self):
self.client.post("/api/order", json={
"symbol": "BTC/USDT",
"side": "buy",
"price": 50000
})
运行命令:
locust -f locustfile.py --host=http://localhost:8080
然后打开浏览器访问 http://localhost:8089,就能看到 Web UI 了。
避坑指南:
- 压测前一定要预热。我习惯先跑1分钟,等数据稳定后再开始记录。
- 不要只压一次。至少跑3次,取中位数或平均值。
- 监控系统资源。如果 CPU 或内存打满了,说明瓶颈在服务端,压测数据才有意义。
知识体系总览
下面这张图是我自己整理的基准测试知识体系,你可以对照着查漏补缺:
这张图把基准测试的核心要素都串起来了。你从方法论出发,搭好环境,选对工具,写好脚本,就能拿到一份可靠的基准数据。
最后说一句:基准测试不是一次性的工作。每次代码变更、配置调整、架构升级后,都应该重新跑一遍。我习惯把基准测试脚本放在 CI 流水线里,每次合并代码前自动跑一次,数据异常就直接阻断发布。