性能基准测试:基准测试方法论、测试环境搭建、压测工具选型(wrk、JMeter、Locust)、测试脚本编写

性能基准测试,说白了就是给你的系统拍一张「体检照」。

我刚开始做报价路由系统时,总觉得上线后跑得挺快。直到有一次,某大客户投诉说「你们的报价响应时间怎么忽高忽低?」。我查了半天,发现是压测环境没搭好,数据全是假的。嗯,从那以后,我每次做性能调优,第一件事就是先把基准测试做扎实。

基准测试方法论:先定标准,再谈优化

基准测试不是随便跑几个请求就完事了。它有一套成熟的方法论,我习惯把它拆成三步:

  1. 定义关键指标:响应时间(RT)、吞吐量(TPS/QPS)、错误率、资源利用率(CPU、内存、IO)。
  2. 确定测试场景:单接口压测、混合场景压测、稳定性压测(长时间运行)。
  3. 建立基线:在相同环境下跑一次,记录数据作为后续优化的参照。

核心原则:基准测试的结果必须可复现。如果两次跑出来的数据差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 文件。核心步骤:

  1. 添加「线程组」:设置并发数、Ramp-Up 时间、循环次数
  2. 添加「HTTP 请求默认值」:配置协议、域名、端口
  3. 添加「HTTP 请求」:配置具体接口路径、参数
  4. 添加「监听器」:聚合报告、响应时间图

如果你需要参数化,可以用 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 或内存打满了,说明瓶颈在服务端,压测数据才有意义。

知识体系总览

下面这张图是我自己整理的基准测试知识体系,你可以对照着查漏补缺:

性能基准测试 方法论 定义关键指标 确定测试场景 建立基线 可复现性 环境搭建 压测机独立部署 服务与生产同规格 数据库隔离 网络环境稳定 工具选型 wrk:轻量高性能 JMeter:功能全面 Locust:Python友好 按场景选择 脚本编写 预热1分钟 跑3次取中位数 监控系统资源

这张图把基准测试的核心要素都串起来了。你从方法论出发,搭好环境,选对工具,写好脚本,就能拿到一份可靠的基准数据。

最后说一句:基准测试不是一次性的工作。每次代码变更、配置调整、架构升级后,都应该重新跑一遍。我习惯把基准测试脚本放在 CI 流水线里,每次合并代码前自动跑一次,数据异常就直接阻断发布。


专注资料整理