wrk 实战:从安装到结果解读
说实话,压测工具我用了不少。ab、siege、JMeter 都折腾过。但要说轻量、高效、适合快速验证吞吐性能的,wrk 绝对排前三。它不像 JMeter 那么重,也不像 ab 那样功能单一。你想想看,一个单机工具能轻松压出几十万 QPS,这本身就说明它的设计很硬核。
这一章,我们就来把 wrk 彻底吃透。从安装开始,到参数调优,再到 Lua 脚本扩展,最后把那一堆输出结果看懂。嗯,都是实战干货。
1. 安装 wrk:别踩编译的坑
wrk 的安装其实很简单。但我在项目中遇到过几次编译失败的情况,基本都是因为缺少依赖。
Mac 用户:
brew install wrk
Linux 用户:
git clone https://github.com/wg/wrk.git
cd wrk
make
libssl-dev 和 libz-dev。我曾经在一台 CentOS 7 上直接 make,结果报错找不到 openssl。后来装了 yum install openssl-devel 才搞定。Ubuntu 的话是 apt install libssl-dev。
编译完直接就能用。我把 ./wrk 扔到 /usr/local/bin 里,全局访问。
2. 基础命令:先跑起来再说
先看一个最简单的例子:
wrk -t12 -c400 -d30s http://localhost:8080/api/test
这条命令的意思是:用 12 个线程,保持 400 个并发连接,持续压 30 秒。我刚开始用 wrk 时,习惯先跑个短时间的看看大概情况,比如 -d10s。等确认服务没问题,再上长时间的压力测试。
常用参数就这几个:
| 参数 | 含义 | 我的建议 |
|---|---|---|
-t |
线程数 | 一般设为 CPU 核心数的 2-4 倍 |
-c |
并发连接数 | 从 100 开始,逐步往上加 |
-d |
压测时长 | 至少 30 秒,太短数据不稳定 |
--latency |
输出延迟分布 | 我每次都加,方便看 P99 |
--timeout 5s。我遇到过 TLS 握手超时导致结果偏低的情况,加了超时参数后数据就正常了。
3. 线程与连接数调优:找到那个平衡点
很多人一上来就问:「线程设多少合适?」其实没有标准答案。它取决于你的机器、网络、以及目标服务的处理能力。
我个人习惯这样调:
- 先固定线程数,比如
-t4。 - 逐步增加并发连接数:
-c100→-c200→-c500。 - 观察 QPS 和延迟。当 QPS 不再明显增长,而延迟开始飙升时,就是瓶颈了。
举个例子。我之前压一个 Java 服务,4 核 8G 的机器:
| 线程数 | 并发连接 | QPS | 平均延迟 | P99 延迟 |
|---|---|---|---|---|
| 4 | 100 | 12,500 | 8ms | 15ms |
| 4 | 300 | 18,200 | 16ms | 32ms |
| 4 | 500 | 19,100 | 26ms | 58ms |
| 8 | 500 | 19,300 | 25ms | 55ms |
看到没?从 300 到 500 并发,QPS 几乎没涨,延迟却翻倍了。这时候再增加线程也没用,说明服务端已经到极限了。
4. Lua 脚本扩展:让压测更灵活
wrk 默认只做简单的 GET 请求。但实际场景哪有这么简单?登录、POST 数据、带 Token……这时候就需要 Lua 脚本了。
先看一个最常用的例子——POST 请求:
wrk.method = "POST"
wrk.body = '{"username":"test","password":"123456"}'
wrk.headers["Content-Type"] = "application/json"
保存为 post.lua,然后执行:
wrk -t4 -c200 -d30s -s post.lua http://localhost:8080/login
更复杂一点的场景,比如每次请求都带不同的 Token。我遇到过需要压测带 JWT 的接口,Token 过期时间又短。这时候可以用 setup 函数提前生成一批 Token:
token_pool = {}
function setup(thread)
-- 每个线程初始化时,从池子里拿一个 Token
thread:set("token", token_pool[thread.id])
end
function request()
wrk.headers["Authorization"] = "Bearer " .. token
return wrk.format("GET", "/api/user/info")
end
response() 回调里处理逻辑太复杂。记住,Lua 脚本里尽量只做简单的数据拼接和格式化,复杂的逻辑放到外面去。
wrk 的 Lua API 其实就几个:
wrk.format(method, path, headers, body)— 构造请求wrk.lookup(host, port)— DNS 解析wrk.connect(addr)— 建立连接response(status, headers, body)— 处理响应
说实话,这些 API 足够覆盖 90% 的压测场景了。
5. 结果解读:别只看 QPS
跑完 wrk,你会看到类似这样的输出:
Running 30s test @ http://localhost:8080/api/test
12 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 12.34ms 8.56ms 123.45ms 75.23%
Req/Sec 3,456.78 567.89 5,678.90 68.90%
Latency Distribution
50% 10.23ms
75% 15.67ms
90% 22.34ms
99% 45.67ms
1234567 requests in 30.00s, 1.23GB read
Requests/sec: 41152.23
Transfer/sec: 41.23MB
很多人只看最后一行 Requests/sec。但说实话,这远远不够。我一般会看这几个指标:
| 指标 | 关注点 | 我的经验 |
|---|---|---|
| Avg Latency | 平均延迟 | 如果超过 100ms,用户体验就很差了 |
| P99 Latency | 99% 请求的延迟 | 这个比平均值重要得多。P99 高说明有长尾请求 |
| Req/Sec 的 Stdev | 每秒请求数的标准差 | 如果很大,说明系统不稳定,有抖动 |
| Transfer/sec | 吞吐带宽 | 如果接近网卡上限,说明瓶颈在网络 |
Socket errors: connect 0, read 5, write 0, timeout 12,说明服务端已经扛不住了。这时候的 QPS 数据其实是「假象」,因为很多请求根本没被处理。我曾经被这个坑过,以为系统能扛 5 万 QPS,结果一看错误率 20%……
还有一个容易被忽略的点:+/- Stdev 后面的百分比。比如 75.23%,意思是 75.23% 的请求延迟在平均值附近。如果这个值很低,说明延迟分布很散,系统不够稳定。
知识体系总览
下面这张图,把 wrk 的核心知识点串起来了。你可以把它当作一个检查清单,看看自己掌握了哪些。