第三章 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/
注意: 我曾经在CentOS上遇到过openssl版本不兼容的问题。如果你也碰到编译报错,试试 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
我的习惯: 在response函数里不要做太重的逻辑,比如解析JSON。压测时CPU资源很宝贵,尽量轻量化。我一般只检查状态码和响应时间。

进阶:模拟多用户

你想想看,真实场景下每个用户的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脚本。
避坑指南: 我曾经在生产环境压测时,直接把并发数设到了2000,结果把数据库打挂了。记住,压测前一定要确认目标服务的承载上限,最好先从低并发开始,逐步加压。

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%的用户体验就很差了。

核心观点: 别只看平均值。我见过太多人报告里QPS很高,但TP99惨不忍睹。真正的优化目标应该是:在保证TP99可控的前提下,提升QPS。

3.5 实战中的常见问题

  • 压测机本身成为瓶颈:如果压测机CPU跑满,结果就不准了。我一般会监控压测机的CPU和内存,确保资源充足。
  • 网络延迟干扰:尽量在同机房或同区域压测,避免公网波动影响结果。
  • 预热不足:很多服务有缓存或JIT编译,刚开始压测时数据偏低。我习惯先跑10秒预热,再记录正式数据。

3.6 本章小结

wrk这个工具,说白了就是一把快刀。安装简单、脚本灵活、报告直观。但工具只是手段,关键是你能不能从报告里读出问题所在。平均延迟、TP99、错误率、吞吐量——这四个指标要结合起来看。

嗯,我记得有一次压测一个搜索接口,平均延迟只有30ms,但TP99到了500ms。后来一查,是某个分词逻辑在极端情况下触发了全表扫描。这就是压测的价值——把隐藏的问题挖出来。

下一章咱们会深入显存优化,但今天的内容你最好亲手跑一遍。纸上得来终觉浅,绝知此事要躬行。

wrk压测实战流程 步骤1:安装wrk 源码编译 / brew 步骤2:编写Lua脚本 初始化 / 请求 / 响应 步骤3:执行压测 wrk -t12 -c400 -d30s 步骤4:解读报告 QPS / 延迟 / 错误率 关键参数说明 -t:线程数(CPU核心数×2) -c:并发连接数(逐步加压) -d:持续时间(建议30s+) --latency:输出延迟分布 核心关注指标 ① 平均延迟 & TP99 ② QPS(吞吐量) ③ Socket errors(错误率) 目标:在TP99可控的前提下,最大化QPS

可可精品资料库