第三章: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没装全。

我的习惯: 我会把wrk直接放在压测机的/opt/wrk目录下,然后做个软链接到/usr/local/bin。这样后续升级版本时,只需要替换目录里的二进制文件就行。

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
核心思路: 用Lua脚本模拟真实用户行为。不要所有请求都打同一个参数,那样压出来的结果没有参考价值。

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

你想想看,这样压出来的数据,才更接近线上真实情况。我在项目中遇到过很多次,用固定参数压测时接口表现很好,一换成动态参数,性能直接腰斩。为什么?因为缓存失效了,每次都要查数据库。

避坑指南: 我曾经有一次压测,忘了关掉服务端的日志打印。结果压测结果惨不忍睹,QPS只有正常值的十分之一。排查了半天,发现是日志刷盘把IO打满了。压测前,一定要检查服务端的日志级别,最好调到WARN或ERROR。

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%的请求在平均值附近,那就说明系统抖动严重,可能有锁竞争或资源争抢。

我个人习惯,拿到报告后先看三个东西:

  1. QPS:能不能满足业务需求?
  2. P99延迟:虽然报告里没直接给,但我会用Max值大致估算。如果Max是Avg的4倍以上,说明有长尾延迟。
  3. 错误率:如果报告里没有显示错误,不代表没有。我会在Lua脚本里加错误统计。
我的经验: 压测报告不是看一次就完事的。我会做多轮压测,每轮调整一个参数(比如并发数从100调到200),然后对比报告。如果QPS不再增长,说明系统已经达到瓶颈了。

最后,用一张图来总结本章的知识体系:

wrk压测全流程 1. 安装wrk 编译安装 / brew 2. 编写Lua脚本 动态参数 / 认证 3. 执行压测 -t / -c / -d 参数 4. 解读报告 QPS / 延迟 / 错误 关键参数说明 -t:线程数(通常=CPU核心数) -c:并发连接数(逐步增加) -d:压测持续时间(建议≥30s) -s:指定Lua脚本路径 注意:-c 不要超过系统最大文件句柄数 报告解读要点 ① QPS:核心吞吐指标 ② Latency Avg:平均延迟 ③ Latency Max:最大延迟(关注长尾) ④ Stddev:标准差(稳定性) ⑤ 错误率:必须为0或接近0

这张图把整个流程串起来了。从安装wrk开始,到编写Lua脚本,再到执行压测,最后解读报告。每一步都有坑,每一步也都有技巧。

好了,这一章的内容就到这里。记住,压测不是跑一次就完事的,它是一个持续迭代的过程。多跑几轮,多对比几份报告,你才能真正摸透系统的性能边界。

可可精品资料库