wrk HTTP 压测脚本与结果解读
更新时间:2026-09-18。wrk 是轻量高并发 HTTP 基准工具,靠极少线程+事件循环打出很高压力。gRPC 压测见 ghz,压测中防雪崩见 服务雪崩防护。
一、基本发压
bash
# 2 线程、100 连接、持续 30 秒
wrk -t2 -c100 -d30s http://localhost:8080/api/list
# 带 header、超时、打印延迟分布
wrk -t4 -c200 -d30s --latency --timeout 5s \
-H 'Authorization: Bearer xxx' \
http://localhost:8080/api/profile参数:-t 事件循环线程数(一般等于压测机 CPU 核数,不是越大越好)、-c 总连接数、-d 时长、-s Lua 脚本、--latency 输出分位延迟。
二、结果怎么读
bash
Latency Distribution
50% 3.20ms
90% 9.80ms
99% 45.00ms # p99,比平均值更能反映尾部卡顿
Requests/sec: 18234.56 # 吞吐 RPS
Socket errors: connect 0, read 0, write 0, timeout 12
Non-2xx or 3xx responses: 300- Requests/sec:服务实际承受的吞吐。
- Latency p50/p90/p99:尾延迟,重点看 p99/p999,而不是平均。
- Socket errors:
connect堆积说明积压/拒绝;timeout多说明请求处理超过--timeout或压测端/服务端到极限。 - Non-2xx:有错误响应时 RPS 再高也没意义,先把错误率降到可接受。
逐步加 -c(50→100→200→400),找拐点:RPS 不再增长而 p99/错误率开始飙升,即服务容量上限。
三、Lua 脚本:POST / 动态参数
lua
-- post.lua
wrk.method = "POST"
wrk.headers["Content-Type"] = "application/json"
wrk.body = '{"name":"perf","age":20}'
-- 需要每请求不同(如唯一 id、token)用下面的回调
local counter = 0
request = function()
counter = counter + 1
local body = string.format('{"id":%d}', counter)
return wrk.format("POST", "/api/order", nil, body)
end
response = function(status, headers, body)
if status ~= 200 then print("status="..status) end
endbash
wrk -t4 -c100 -d30s --latency -s post.lua http://localhost:8080常见坑
- 连接数 ≠ 并发用户数:
-c是保持的 TCP 连接数;真实用户有思考时间,模拟用户级并发要用 RPS=并发数/思考时间换算,或换更贴近业务的模型。 - 压测机先成为瓶颈:wrk 很高效,但单核打满、端口耗尽(TIME_WAIT)、客户端带宽都会让结果失真;用多台发压机并监控压测端。
- 短连接打满端口:默认走长连接复用;大量 connect 错误时关注压测端临时端口与
somaxconn,别误判成服务端问题。 - 只看平均延迟:必须
--latency看 p99/p999,平均会掩盖尾部。 - GET 打写接口:压测下单/扣库存要用 Lua 发 POST 并构造唯一参数,否则命中缓存或幂等去重,测出虚高数字。
- 不带超时:卡住的请求会一直占连接,加
--timeout才能暴露真延迟。
总结
wrk 用少量事件循环线程(-t,约等于压测机核数)和一组长连接(-c)持续发压(-d),重点读 Requests/sec 吞吐与 --latency 给出的 p99/p999 尾延迟,同时必须看 Socket errors 的 connect/timeout 和 Non-2xx 错误率,通过逐级加大 -c 找吞吐不再增而尾延迟和错误率飙升的容量拐点;POST、动态唯一参数和状态校验用 Lua 的 wrk.method/body 与 request/response 回调实现;务必记住 -c 是连接数而非有思考时间的真实用户、要先排除压测机 CPU 与端口耗尽瓶颈、用 --timeout 暴露真实延迟,并对写接口构造唯一请求体避免被缓存或幂等机制“测出虚高”。