第三章 wrk实战:安装、脚本编写与压测报告解读
好,咱们进入实战环节。wrk这个工具,说实话是我压测工具箱里用得最顺手的之一。它轻量、高效,尤其适合快速验证接口的吞吐能力。今天我就带你完整走一遍——从安装到写Lua脚本,再到压测一个真实HTTP接口,最后把报告里的门道给你讲透。
3.1 安装wrk:别踩这些坑
安装本身不复杂,但我见过不少人在这一步翻车。先说说主流系统的安装方式。
Linux(以Ubuntu为例)
# 从源码编译,我推荐这种方式
sudo apt-get update
sudo apt-get install build-essential libssl-dev git -y
git clone https://github.com/wg/wrk.git
cd wrk
make
# 把二进制放到PATH里
sudo cp wrk /usr/local/bin/
yum install openssl-devel 后再make。
macOS
brew install wrk
嗯,mac用户就省心多了。不过我个人习惯还是用Linux做压测,毕竟生产环境大多是Linux。
验证安装
wrk --version
看到版本号输出,就说明装好了。如果提示找不到命令,检查一下PATH或者是不是编译失败了。
3.2 编写Lua脚本:让压测更灵活
wrk本身支持简单的GET请求压测,但真实场景哪有这么简单?你需要自定义请求头、POST数据、甚至模拟用户登录态。这时候Lua脚本就派上用场了。
我习惯把Lua脚本分成三部分:初始化、请求生成、响应处理。下面是一个实战中常用的模板。
基础脚本模板
-- 压测脚本: benchmark.lua
-- 初始化阶段,只执行一次
wrk.method = "POST"
wrk.body = '{"user_id": 12345, "action": "query"}'
wrk.headers["Content-Type"] = "application/json"
wrk.headers["Authorization"] = "Bearer your_token_here"
-- 每个请求都会调用这个函数
request = function()
-- 你可以在这里动态修改请求参数
-- 比如从列表中取不同的user_id
return wrk.format("POST", "/api/v1/query", nil, wrk.body)
end
-- 每个响应回来都会调用这个函数
response = function(status, headers, body)
-- 可以在这里做简单的校验
if status ~= 200 then
-- 记录非200的响应,方便排查
print("非200状态码: " .. status)
end
end
进阶:模拟多用户
你想想看,真实场景下每个用户的token都不一样。怎么模拟?用数组。
-- 模拟100个不同用户
tokens = {}
for i = 1, 100 do
tokens[i] = "token_" .. i
end
request = function()
-- 取模轮询,模拟不同用户
local idx = math.random(1, 100)
wrk.headers["Authorization"] = "Bearer " .. tokens[idx]
return wrk.format("GET", "/api/v1/user/info")
end
这里用了math.random,但要注意——wrk是多线程的,随机数生成可能会有竞争。我建议在初始化阶段就生成好随机序列,避免运行时开销。
3.3 压测一个HTTP接口
脚本写好了,咱们来压测一个真实的接口。假设目标接口是 http://api.example.com/v1/search,POST请求,需要传关键词。
执行压测命令
wrk -t12 -c400 -d30s --latency -s benchmark.lua http://api.example.com/v1/search
参数解释:
-t12:12个线程。我一般根据CPU核心数来定,通常是核心数的2倍。-c400:400个并发连接。这个值需要根据接口能力调整,别一上来就搞太大。-d30s:压测持续30秒。太短了数据不稳定,太长了浪费时间。30秒是个不错的起点。--latency:输出延迟分布,这个很重要,后面解读报告时会用到。-s benchmark.lua:指定我们写的Lua脚本。
3.4 解读压测报告
压测跑完了,终端会输出一份报告。很多人只看一个QPS就完事了,其实里面信息量很大。咱们逐行拆解。
典型输出示例
Running 30s test @ http://api.example.com/v1/search
12 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 45.23ms 12.34ms 567.89ms 85.67%
Req/Sec 823.45 102.34 1234.56 75.23%
24678 requests in 30.00s, 4.52MB read
Socket errors: connect 0, read 3, write 0, timeout 1
Requests/sec: 822.60
Transfer/sec: 154.12KB
关键指标解读
| 指标 | 含义 | 我的经验 |
|---|---|---|
| Latency Avg | 平均延迟,45.23ms | 如果超过200ms,用户侧体验就会变差。我一般会结合TP99来看。 |
| Latency Stdev | 延迟标准差,12.34ms | 这个值越小越好。如果很大,说明延迟波动剧烈,可能有慢查询或GC问题。 |
| Req/Sec Avg | 每秒请求数,823.45 | 这就是吞吐量。但注意,这是平均值,要看峰值能不能扛住。 |
| Socket errors | 连接错误、读错误、超时等 | 只要有非0的错误,就要排查。我遇到过read错误是因为后端主动断开了连接。 |
| Requests/sec | 整体QPS,822.60 | 这是最终的总吞吐。结合并发数,可以算出单连接的处理能力。 |
延迟分布(Latency Distribution)
如果你加了--latency参数,还会看到类似这样的输出:
Latency Distribution
50% 42.00ms
75% 48.00ms
90% 55.00ms
99% 78.00ms
这里有个关键点:TP99(99分位延迟)。平均延迟45ms看起来不错,但TP99到了78ms,说明有1%的请求延迟较高。如果TP99超过1秒,那这1%的用户体验就很差了。
3.5 实战中的常见问题
- 压测机本身成为瓶颈:如果压测机CPU跑满,结果就不准了。我一般会监控压测机的CPU和内存,确保资源充足。
- 网络延迟干扰:尽量在同机房或同区域压测,避免公网波动影响结果。
- 预热不足:很多服务有缓存或JIT编译,刚开始压测时数据偏低。我习惯先跑10秒预热,再记录正式数据。
3.6 本章小结
wrk这个工具,说白了就是一把快刀。安装简单、脚本灵活、报告直观。但工具只是手段,关键是你能不能从报告里读出问题所在。平均延迟、TP99、错误率、吞吐量——这四个指标要结合起来看。
嗯,我记得有一次压测一个搜索接口,平均延迟只有30ms,但TP99到了500ms。后来一查,是某个分词逻辑在极端情况下触发了全表扫描。这就是压测的价值——把隐藏的问题挖出来。
下一章咱们会深入显存优化,但今天的内容你最好亲手跑一遍。纸上得来终觉浅,绝知此事要躬行。