第2章:测试工具选型——JMeter、Locust、wrk 怎么选?

做吞吐测试这么多年,我经常被问到同一个问题:「到底该用哪个工具?」

说实话,没有银弹。每个工具都有自己的脾气。你想想看,一个做 HTTP 接口压测的场景,跟一个测 WebSocket 长连接、或者测数据库连接池的场景,工具选型能一样吗?

这一章,我就把 JMeter、Locust、wrk 这三个主流工具掰开揉碎讲清楚。最后再给出一套我自己的选型策略,帮你少走弯路。

核心观点:选工具不是选「最好的」,而是选「最适合当前场景的」。吞吐测试的成败,一半在工具选型,一半在脚本设计。

吞吐测试工具选型决策框架 测试场景 复杂业务流程 / GUI 高并发 / 动态参数 纯吞吐 / 低延迟 推荐:JMeter 推荐:Locust 推荐:wrk 关键考量 断言丰富 / 插件生态 学习曲线中等 关键考量 Python 脚本灵活 分布式原生支持 关键考量 C 语言内核 / 极高性能 仅支持 HTTP

2.1 JMeter 基础

JMeter 是 Apache 旗下的老牌压测工具。我入行时它就已经很成熟了。它的核心优势在于:图形化界面 + 丰富的协议支持。

你不需要写一行代码,就能搭建出一个包含登录、查询、下单的复杂业务流程压测脚本。对于测试团队里非开发背景的成员来说,这简直是福音。

我的习惯:但凡涉及多步骤、有状态交互(比如需要先登录拿 token,再带 token 请求后续接口)的场景,我第一反应就是 JMeter。它的「正则表达式提取器」和「JSON 提取器」在处理动态参数时非常顺手。

一个典型的 JMeter 测试计划结构如下:

Test Plan
 ├── Thread Group (线程组:配置并发数、Ramp-Up 时间)
 │    ├── HTTP Request Defaults (默认请求配置)
 │    ├── HTTP Cookie Manager (Cookie 管理器)
 │    ├── CSV Data Set Config (参数化:从文件读取数据)
 │    ├── 登录请求
 │    │    └── JSON Extractor (提取返回的 token)
 │    ├── 业务查询请求
 │    │    └── Response Assertion (断言:检查返回码)
 │    └── 下单请求
 │         └── Duration Assertion (断言:检查响应时间)
 └── Listeners (监听器:聚合报告、图形结果、查看结果树)

不过 JMeter 也有短板。我记得有一次压测一个高吞吐的网关服务,单机 JMeter 只能压到 3000 QPS,再往上就报 OOM 了。后来换成 wrk,同样一台机器轻松跑到 2 万 QPS。嗯,这里要注意:JMeter 的 Java 虚拟机开销很大,不适合极高性能场景。

2.2 Locust 基础

Locust 是后来崛起的 Python 系压测工具。它的设计理念很特别——用代码定义用户行为。

你写一个 Python 类,继承 HttpUser,然后在里面定义 @task 装饰的方法。每个方法代表一个用户操作。Locust 会为每个虚拟用户启动一个协程(gevent),模拟真实用户的行为模式。

我个人很喜欢 Locust 的一点是:它的 Web UI 是实时更新的,你可以一边跑压测,一边动态调整并发数,不用停掉测试。这在调试阶段特别有用。

一个简单的 Locust 脚本示例:

from locust import HttpUser, task, between

class WebsiteUser(HttpUser):
    wait_time = between(1, 5)  # 用户等待时间 1~5 秒

    @task(3)  # 权重 3
    def view_homepage(self):
        self.client.get("/")

    @task(1)  # 权重 1
    def search_product(self):
        self.client.post("/search", json={"keyword": "手机"})

    def on_start(self):
        # 每个用户启动时执行一次:模拟登录
        self.client.post("/login", json={"user": "test", "pass": "123"})

我曾经踩过的坑:Locust 的协程模型虽然轻量,但如果你在任务方法里做了 CPU 密集型的操作(比如加解密、大量正则匹配),它会阻塞整个协程调度器,导致压测结果失真。所以 Locust 更适合 I/O 密集型的场景,比如 HTTP 请求、数据库查询。

2.3 wrk 基础

wrk 是一个「小而美」的工具。它用 C 语言编写,基于 Redis 作者开发的 ae 事件循环库。它的设计目标只有一个:压出被测系统的极限吞吐。

wrk 没有 GUI,没有脚本语言,没有断言。它就是一个命令行工具,你给它一个 URL、一个并发数、一个持续时间,它就拼命发请求。它的输出也很简洁:延迟分布、QPS、错误率。

一个典型的 wrk 命令:

wrk -t12 -c400 -d30s --latency http://target-server.com/api/hello

# 参数说明:
# -t12      : 启动 12 个线程
# -c400     : 保持 400 个并发连接
# -d30s     : 持续压测 30 秒
# --latency : 打印延迟分布(P50, P75, P90, P99)

输出示例:

Running 30s test @ http://target-server.com/api/hello
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    12.34ms    8.56ms 198.76ms   85.23%
    Req/Sec     3.45k   456.78     5.21k    72.10%
  Latency Distribution
     50%   10.12ms
     75%   15.34ms
     90%   22.56ms
     99%   45.78ms
  1234567 requests in 30.00s, 1.23GB read
Requests/sec:  41152.23
Transfer/sec:     41.98MB

看到那个 Requests/sec: 41152.23 了吗?这就是 wrk 的威力。同样的机器,JMeter 可能只能压到 3000,wrk 能到 4 万。差距就在底层实现上。

我的建议:wrk 最适合做「基准测试」。比如上线前,先用 wrk 跑一轮,看看系统裸吞吐是多少。如果 wrk 都压不上去,那肯定是系统本身有瓶颈,跟压测工具无关。

2.4 工具对比

我把三个工具的核心差异整理成了表格,方便你对照:

维度 JMeter Locust wrk
开发语言 Java Python C
脚本方式 GUI 拖拽 + XML Python 代码 命令行参数 + Lua
协议支持 HTTP、JDBC、JMS、FTP 等 20+ HTTP / HTTPS(可扩展) HTTP / HTTPS 仅限
单机吞吐能力 中等(受 JVM 限制) 较高(协程模型) 极高(C 原生事件循环)
分布式支持 支持(Master-Slave) 原生支持(Master-Worker) 不支持(需外部工具)
断言能力 丰富(响应码、内容、时长) 需自行编写 无
实时监控 监听器 + 插件 Web UI 实时更新 终端实时输出
学习曲线 低(GUI 操作) 中(需 Python 基础) 低(命令行简单)
典型场景 复杂业务流程、接口自动化 高并发、动态参数、持续集成 基准测试、极限吞吐验证

2.5 选型策略

看完对比,你可能更纠结了——「到底用哪个?」

别急,我总结了一套自己的选型策略,按场景来分:

  1. 场景一:复杂业务流程压测
    • 比如电商下单、银行开户,涉及多个接口串联、参数传递、断言验证。
    • 选 JMeter。 它的 GUI 和丰富的提取器/断言器,能让你快速搭建脚本。
  2. 场景二:高并发 API 压测 + 持续集成
    • 比如微服务网关、消息推送接口,需要动态调整并发,且要集成到 CI/CD 流水线。
    • 选 Locust。 Python 脚本天然适合版本管理,Web UI 方便调试,分布式扩展也简单。
  3. 场景三:极限吞吐基准测试
    • 比如上线前验证系统能扛多少 QPS,或者对比不同版本、不同配置的性能差异。
    • 选 wrk。 简单、暴力、高效。你不需要断言,只需要知道「能不能扛住」。
  4. 场景四:混合场景
    • 比如先用 wrk 摸高,再用 JMeter 或 Locust 做精细化压测。
    • 我经常这么干:wrk 跑一轮,发现瓶颈在数据库;然后换 JMeter 写一个包含登录、查询、更新的混合脚本,验证优化效果。

我的选型口诀:「复杂业务用 JMeter,灵活并发找 Locust,极限吞吐上 wrk。三个工具轮着用,没有搞不定的压测。」

最后说一句:工具只是手段,不是目的。我见过有人用 wrk 压测一个需要登录的接口,结果所有请求都返回 401,他还纳闷为什么 QPS 这么高。嗯,工具选对了,脚本写错了,一样白搭。