第2章:测试工具选型——JMeter、Locust、wrk 怎么选?
做吞吐测试这么多年,我经常被问到同一个问题:「到底该用哪个工具?」
说实话,没有银弹。每个工具都有自己的脾气。你想想看,一个做 HTTP 接口压测的场景,跟一个测 WebSocket 长连接、或者测数据库连接池的场景,工具选型能一样吗?
这一章,我就把 JMeter、Locust、wrk 这三个主流工具掰开揉碎讲清楚。最后再给出一套我自己的选型策略,帮你少走弯路。
核心观点:选工具不是选「最好的」,而是选「最适合当前场景的」。吞吐测试的成败,一半在工具选型,一半在脚本设计。
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 选型策略
看完对比,你可能更纠结了——「到底用哪个?」
别急,我总结了一套自己的选型策略,按场景来分:
- 场景一:复杂业务流程压测
- 比如电商下单、银行开户,涉及多个接口串联、参数传递、断言验证。
- 选 JMeter。 它的 GUI 和丰富的提取器/断言器,能让你快速搭建脚本。
- 场景二:高并发 API 压测 + 持续集成
- 比如微服务网关、消息推送接口,需要动态调整并发,且要集成到 CI/CD 流水线。
- 选 Locust。 Python 脚本天然适合版本管理,Web UI 方便调试,分布式扩展也简单。
- 场景三:极限吞吐基准测试
- 比如上线前验证系统能扛多少 QPS,或者对比不同版本、不同配置的性能差异。
- 选 wrk。 简单、暴力、高效。你不需要断言,只需要知道「能不能扛住」。
- 场景四:混合场景
- 比如先用 wrk 摸高,再用 JMeter 或 Locust 做精细化压测。
- 我经常这么干:wrk 跑一轮,发现瓶颈在数据库;然后换 JMeter 写一个包含登录、查询、更新的混合脚本,验证优化效果。
我的选型口诀:「复杂业务用 JMeter,灵活并发找 Locust,极限吞吐上 wrk。三个工具轮着用,没有搞不定的压测。」
最后说一句:工具只是手段,不是目的。我见过有人用 wrk 压测一个需要登录的接口,结果所有请求都返回 401,他还纳闷为什么 QPS 这么高。嗯,工具选对了,脚本写错了,一样白搭。