第三章:wrk实战——从安装到压测报告解读
说实话,wrk是我个人最常用的压测工具之一。为什么?因为它轻量、高效,而且配合Lua脚本几乎能模拟任何业务场景。我记得刚带团队做性能优化那会儿,很多同事还在用ab(Apache Bench)做压测,结果压出来的数据跟线上表现完全对不上。后来换成wrk,问题一下子就暴露了。
这一章,我们就来完整走一遍wrk的实战流程。从安装开始,到编写Lua脚本,再到压测一个真实的HTTP接口,最后把那份压测报告彻底读懂。
3.1 安装wrk:三分钟搞定
wrk的安装非常简单。我习惯在Linux环境下做压测,因为wrk本身对Linux的支持最好。如果你用的是Mac,也可以用Homebrew装。
# Ubuntu/Debian
sudo apt-get install build-essential libssl-dev git -y
git clone https://github.com/wg/wrk.git
cd wrk
make
# 把wrk放到系统路径
sudo cp wrk /usr/local/bin/
# Mac用户
brew install wrk
装完之后,跑一下 wrk --version。如果能看到版本号,说明装好了。嗯,这里要注意:wrk依赖OpenSSL,如果你在编译时报错,多半是libssl-dev没装全。
3.2 编写Lua脚本:让压测更真实
wrk本身支持简单的GET/POST压测,但真实场景哪有那么简单?比如你需要登录拿到token,或者需要构造特定的请求体。这时候,Lua脚本就派上用场了。
我曾经遇到过一个项目,接口需要先调用登录接口拿到session,然后带着这个session去压测业务接口。用wrk的Lua脚本,我只需要几十行代码就搞定了。
先看一个最基础的Lua脚本模板:
-- request.lua
wrk.method = "POST"
wrk.body = '{"user_id": 123, "action": "query"}'
wrk.headers["Content-Type"] = "application/json"
-- 每个请求都会执行这个函数
response = function(status, headers, body)
if status ~= 200 then
print("请求失败,状态码:" .. status)
end
end
这个脚本做了三件事:设置请求方法、设置请求体、设置请求头。然后每个请求返回后,检查状态码是不是200。
但说实话,这还不够。真实场景中,我们往往需要动态生成请求参数。比如压测一个商品详情接口,每次请求的商品ID不能一样。这时候可以用 init 函数:
-- dynamic_request.lua
local counter = 0
init = function(args)
-- 初始化计数器
counter = 0
end
request = function()
counter = counter + 1
local product_id = 10000 + counter
wrk.method = "GET"
wrk.path = "/api/product/" .. product_id
return wrk.format(nil)
end
3.3 压测一个HTTP接口:实战演练
假设我们有一个用户查询接口:GET /api/user?id=xxx。现在要压测这个接口的吞吐能力。
我会先做一轮小规模压测,看看基本情况:
wrk -t4 -c100 -d30s http://your-api-server.com/api/user?id=1
参数说明:
-t4:用4个线程-c100:保持100个并发连接-d30s:压测持续30秒
但这样压,所有请求都打同一个用户ID,显然不合理。我们用刚才的Lua脚本来做:
wrk -t4 -c100 -d30s -s dynamic_request.lua http://your-api-server.com
你想想看,这样压出来的数据,才更接近线上真实情况。我在项目中遇到过很多次,用固定参数压测时接口表现很好,一换成动态参数,性能直接腰斩。为什么?因为缓存失效了,每次都要查数据库。
3.4 解读压测报告:数据会说话
压测跑完之后,wrk会输出一份报告。很多人只看一个QPS就完事了,其实这里面信息量很大。
先看一个典型的输出:
Running 30s test @ http://your-api-server.com
4 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 45.23ms 12.34ms 198.76ms 78.56%
Req/Sec 554.32 89.21 789.00 72.34%
66452 requests in 30.00s, 8.52GB read
Requests/sec: 2215.07
Transfer/sec: 290.87MB
我来逐行拆解:
| 指标 | 含义 | 我的解读 |
|---|---|---|
| Latency Avg | 平均延迟 | 45.23ms,对于API接口来说,这个值在100ms以内都算不错 |
| Latency Max | 最大延迟 | 198.76ms,说明有少数请求比较慢,需要关注是不是有GC停顿 |
| Req/Sec Avg | 每个线程每秒处理的请求数 | 554.32,乘以4个线程就是总QPS |
| Requests/sec | 总QPS | 2215.07,这是核心指标 |
| Transfer/sec | 网络吞吐量 | 290.87MB/s,如果接近网卡上限,说明瓶颈在网络 |
这里有个细节:Latency的Stddev(标准差)。78.56%的请求延迟在平均值附近,说明延迟分布比较集中,系统表现稳定。如果标准差很大,比如只有40%的请求在平均值附近,那就说明系统抖动严重,可能有锁竞争或资源争抢。
我个人习惯,拿到报告后先看三个东西:
- QPS:能不能满足业务需求?
- P99延迟:虽然报告里没直接给,但我会用Max值大致估算。如果Max是Avg的4倍以上,说明有长尾延迟。
- 错误率:如果报告里没有显示错误,不代表没有。我会在Lua脚本里加错误统计。
最后,用一张图来总结本章的知识体系:
这张图把整个流程串起来了。从安装wrk开始,到编写Lua脚本,再到执行压测,最后解读报告。每一步都有坑,每一步也都有技巧。
好了,这一章的内容就到这里。记住,压测不是跑一次就完事的,它是一个持续迭代的过程。多跑几轮,多对比几份报告,你才能真正摸透系统的性能边界。