压力测试工具 wrk 的使用
bash
Ubuntu/Debian
sudo apt update sudo apt install build-essential libssl-dev git -y
CentOS/RHEL
sudo yum groupinstall 'Development Tools' sudo yum install openssl-devel git -y
然后从源码编译安装:
```bash
git clone https://github.com/wg/wrk.git
cd wrk
make
sudo cp wrk /usr/local/bin
在 macOS 上安装
使用 Homebrew 是最简单的方式:
brew install wrk
在 Windows 上使用
wrk 原生不支持 Windows,建议在 WSL (Windows Subsystem for Linux) 中安装 Linux 版本,或使用替代工具(如 bombardier、hey)。
安装完成后,在终端运行 wrk --version 确认安装成功。
基本用法
最简单的压力测试命令如下,它会使用 2 个线程、10 个并发连接,持续测试 10 秒:
wrk -t2 -c10 -d10s https://example.com
输出结果示例:
Running 10s test @ https://example.com
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 45.12ms 89.05ms 1.23s 89.12%
Req/Sec 1.12k 312.43 2.21k 68.50%
22345 requests in 10.01s, 2.78GB read
Requests/sec: 2232.15
Transfer/sec: 284.12MB
输出解读:
- Latency:延迟,即每个请求的响应时间。
- Avg:平均值。
- Stdev:标准差,反映延迟的波动情况。
- Max:最大延迟。
- +/- Stdev:延迟分布在平均值±1个标准差范围内的请求百分比。
- Req/Sec:每秒完成的请求数。
- Requests/sec和Transfer/sec:整个测试周期内每秒完成的请求数与每秒传输的数据量。
常用命令行参数
| 参数 | 说明 |
|---|---|
-t, --threads <N> |
使用的线程数。建议设置为 CPU 核心数的 2-4 倍。 |
-c, --connections <N> |
保持打开的 HTTP 连接总数。每个线程均匀分配连接数。 |
-d, --duration <time> |
测试持续时间,例如 -d 30s、-d 1m。 |
-s, --script <lua_script> |
加载 Lua 脚本,用于自定义请求生成过程。 |
-H, --header <header> |
在请求中添加额外的 HTTP 请求头,可多次使用。 |
--latency |
打印详细的延迟分布信息(百分比)。 |
--timeout <time> |
设置请求超时时间,如 --timeout 2s。 |
使用示例:用 4 个线程、100 个连接测试 30 秒,并输出延迟分布。
wrk -t4 -c100 -d30s --latency https://api.example.com/v1/data
通过 Lua 脚本增强测试
wrk 的强大之处在于可以通过 Lua 脚本在请求的三个生命周期阶段(setup、running、done)注入自定义逻辑。脚本中的 wrk 全局对象提供了 setup、init、request、response、delay、done 等函数。
创建动态请求
例如,每次请求携带一个随机的用户 ID 和自定义请求头:
-- random_request.lua
counter = 0
wrk.method = "POST"
wrk.body = '{"key": "value"}'
wrk.headers["Content-Type"] = "application/json"
function request()
local user_id = math.random(1, 100000)
local path = "/api/user/" .. user_id
counter = counter + 1
-- 返回 wrk 请求路径,会覆盖命令行中的 URL 路径
return wrk.format("GET", path, wrk.headers, wrk.body)
end
function done(summary, latency, requests)
print("-------------------------")
io.write("Script counter: " .. counter .. "\n")
end
运行:
wrk -t2 -c10 -d20s -s random_request.lua https://api.example.com
校验响应内容
使用 response 函数可以在每个响应返回时执行校验,并统计错误:
-- validate.lua
errors = 0
successes = 0
function response(status, headers, body)
if status ~= 200 then
errors = errors + 1
elseif not string.find(body, "expected_text") then
errors = errors + 1
else
successes = successes + 1
end
end
function done(summary, latency, requests)
io.write("Validation errors: " .. errors .. "\n")
io.write("Successes: " .. successes .. "\n")
end
自定义延迟计算(忽略连接建立时间)
有时只关心服务器处理请求的延迟,可以在 setup 阶段标记请求发送时间:
-- precise_latency.lua
wrk.method = "GET"
function init(args)
-- 用于存储每个请求发送时间戳的表
req_time = {}
end
function request()
local idx = 0 -- 这里可以使用线程安全的递增计数器,但简单演示
-- 记录请求发送前的时间
req_time[math.random(1,100000)] = socket.gettime()
return wrk.format()
end
function response(status, headers, body)
-- 实际处理中需要匹配对应的请求,这里仅为思路演示
end
高级技巧与注意事项
1. 合理设置线程与连接数
- 线程过多会带来上下文切换开销,一般设置为 CPU 核心数的 2~4 倍即可。
- 连接数直接影响并发压力,不要设置得过大,以免本地端口被耗尽或触及目标服务器的限制。
- 逐步增加连接数,观察请求速率是否达到瓶颈,当请求速率不再线性增长时,说明目标服务或本地系统已到极限。
2. 针对 HTTPS 测试
wrk 使用 OpenSSL 进行 HTTPS 通信,测试时会消耗较多 CPU 资源。如果目的是测试应用层性能,可以考虑在本地使用反向代理(如 nginx)终结 TLS,然后用 wrk 压测 HTTP 端口,以排除加解密开销。
3. 监控目标服务器状态
压力测试的核心在于观察服务器行为。请务必在测试期间监控目标服务器的 CPU、内存、磁盘 I/O 以及应用级指标(如 QPS、错误率、响应时间分布)。不要仅依赖 wrk 的输出数字,因为它反映的是客户端视角。
4. 避免测试结果失真
- 在局域网或同机器测试时,网络延迟极小,测试结果可能无法反映真实用户体验。
- wrk 是单机负载生成器,如果目标服务非常强大,可能单台 wrk 机器无法压满其吞吐量,此时可使用多台机器同时发起测试,或使用分布式压力测试方案。
5. 使用预热和 ramp-up
wrk 不支持内置的逐步增加并发(ramp-up)功能。如果需要平滑增加负载,可以编写脚本多次调用 wrk,逐步增大 -c 参数,或者使用功能更丰富的工具如 k6。
6. 读懂百分位延迟
使用 --latency 参数可以获得延迟的百分位分布:
Latency Distribution
50% 7.12ms
75% 10.23ms
90% 14.55ms
99% 35.10ms