
简介wrk.tar.gz 是面向后端开发、运维与性能测试人员的高性能 HTTP 基准测试工具经作者预先编译完成解压后即可在终端直接运行省去本地编译依赖快速用于 Web 服务、API 接口及负载均衡器的压力测试。包体约 214.16MB包含可直接执行的 wrk 工具及 LuaJIT 脚本支持组件适配需要自定义请求模式、延迟控制与结果校验的复杂压测场景。工具支持 -c、-d、-t、-r、-s 等参数可灵活调节并发连接数、测试时长、线程数及固定请求速率内置 LuaJIT 脚本接口允许编写 sample.lua 这类脚本模拟多种请求逻辑并输出每秒请求数、传输速率及延迟分布如 50%、90%、99%等关键性能指标便于定位服务瓶颈。目前已有 258 人学习下载适合需要对比服务配置、评估不同技术栈性能或排查优化效果的开发者快速上手。1. 拿到 wrk.tar.gz 之后先认清这包能压什么上线前夜后端同事甩过来一个压缩包wrk.tar.gz 压测工具已经编译完。你要在两小时内确认新接口能扛住多少流量wrk 就是我在这类场景下优先选用的工具。它是 C 语言写的单机 HTTP 压测工具基于 epoll 和多线程模型一台普通压测机就能打出几万到几十万 QPS不需要部署 agent不需要装 JRE拷过去解压就能用。它解决的是「接口到底能扛多少并发、延迟在什么水位开始劣化」这类容量问题适合后端开发、运维和中间件维护者。但也要说清楚边界wrk 只做 HTTP/1.1 协议的压力注入没有场景编排、没有分布式协调复杂业务流程压测不是它的主场。这篇从解压落地讲起覆盖参数设置、Lua 脚本、踩坑记录和结果验证。2. 把编译好的 wrk 跑起来解压、校验和第一条压测命令2.1 解压后先做三个检查二进制、依赖库、PATH拿到手先别急着压花两分钟确认这个「编译完」的包在你当前机器上真的能跑。常见做法是解压后先看一眼目录结构wrk 的发布包通常就是把单个二进制或者带配套脚本的目录打在一起解压命令如下tar xzf wrk.tar.gz cd wrk ls -l wrk ./wrk --versiontar 解压不用解释关键是解压后要执行./wrk --version而不是直接wrk因为当前目录不在 PATH 里。这一步能同时验证两件事二进制有可执行权限、动态库依赖完整。如果报cannot open shared object file说明这台机器上缺 libssl 相关运行库常见做法是装libssl-dev或openssl-devel而不是退回源码重新编译。依赖检查用 ldd 看得更直观ldd wrkldd 会列出二进制运行时依赖的所有.so文件常见的 wrk 发布包依赖 libssl 和 libc看到not found就知道缺哪个包。我一般还会顺手file wrk看一眼是 64 位还是 32 位防止把 x86 的包拷到 ARM 机器上白折腾。确认能跑之后下一步是把它放进 PATH省得每次都要./wrkinstall -m 0755 wrk /usr/local/bin/wrk wrk --versioninstall比cp多做了两件事自动设置可执行权限、按目标路径安装。之后在任何目录直接敲wrk就能用。注意别用 root 去跑压测压测机最好也是普通用户避免被测服务把 root 来源的连接当成异常流量。到这里这个「已经编译完」的包才算真正落地。2.2 最小压测命令压本机服务看懂输出里的每一列工具就绪后第一条命令我建议压自己的本机服务或者预发环境的健康检查接口不要上来就打业务接口。健康接口路径短、没有复杂逻辑压出来的数字能帮你确认 wrk 本身工作正常。wrk -t4 -c400 -d30s --latency http://127.0.0.1:8080/health这条命令的意思是用 4 个线程、保持 400 个并发连接、持续压 30 秒--latency必须带上否则默认不打印延迟百分位分布。压测目标写127.0.0.1避免了 DNS 解析这个变量第一次跑通链路最重要。输出里每一行都值得看懂尤其这两段输出项含义怎么读Latency 分布每个百分位的响应耗时看 Avg 和 99% 两列P99 比 Avg 更能反映尾延迟Req/Sec每秒请求数即 QPS看线程 Avg 和整体 SumSum 是整机 QPSThread Stats每个线程的 Avg/Stdev/MaxStdev 太大说明请求耗时不均匀Socket errorsconnect/read/write/timeout 计数任何计数大于 0 都要排查Non-2xx/3xx responses非预期状态码数量大于 0 说明接口开始报错第一次压完重点不是看 QPS 高低而是确认 Socket errors 是 0、Non-2xx 是 0。出现 connect 错误先查句柄数和半连接队列出现 read 错误查被测服务的连接关闭策略这些都写在第 5 章。健康接口压通之后再换成真实业务路径比如/api/order/list?page1。换路径时注意一点wrk 只支持 HTTP/1.1如果你的服务是 HTTP/2 或者 gRPCwrk 覆盖不了需要换 h2load 这类工具这不是 wrk 的缺陷是协议边界。2.3 如果手里其实是源码包补一条编译路线有人下载的 wrk.tar.gz 其实是源码包解压后没有 wrk 二进制只有 Makefile 和一堆.c文件。这种包编译起来也不复杂wrk 的依赖很简单核心是 OpenSSL 的开发头文件Lua 解释器是源码自带的不需要系统装 Lua。# 先确认编译工具链和 openssl 开发头都齐了 gcc --version openssl version # 在源码目录里直接编译 make -j4-j4表示用 4 个并行任务编译数字改成你机器的 CPU 核数。编译产物就在当前目录名叫 wrk然后按 2.1 的步骤安装即可。如果 make 报错找不到openssl/ssl.h说明缺开发头文件Debian/Ubuntu 系执行apt install libssl-devCentOS/RHEL 系执行yum install openssl-devel。这里有一个值得注意的环境差异一些国产系统默认仓库里的 gcc 版本偏老比如在 Kylin V10 上编译遇到语法不兼容报错时常见做法是装新版本工具链再编或者直接下载别人编好的二进制这也是为什么「已经编译完」的包在很多场景下更省事。编译这步本身不复杂wrk 的构建逻辑一点都不玄学真正花时间的是编译完之后的参数调优也就是下一章的内容。3. 线程、连接、时长怎么定wrk 参数不是越大越能压3.1 线程数先看 CPU 核数别让压测机自己排队wrk 的-t参数控制 worker 线程数每个线程跑一个 epoll 事件循环。这个值不是越大越好线程数超过 CPU 核数后线程调度和上下文切换的开销会把压测机自己的 CPU 先吃满QPS 不升反降。nprocnproc输出当前机器的逻辑核数。拿这个数字做基准QPS 目标在 5 万以下的一般压测-t取核数即可目标更高或者被测服务延迟较大时可以放宽到核数的 1.5 到 2 倍。比如 8 核机器-t8起步最多试到-t16再往上加基本是白费。wrk 的线程模型决定了每个线程要维护一批连接的事件循环线程太多反而让 epoll 分发效率下降这是我在多台机器上对比过的规律不是拍脑袋。3.2 连接数从 100 开始爬坡找到服务的水位而不是打爆网卡-c是 wrk 里最容易拍脑袋的参数。要理解它先得知道 wrk 的连接复用机制wrk 建立的连接默认走 HTTP keep-alive一个连接建好后反复发请求不会每个请求都重新握手。所以-c400的意思是同时保持 400 个活跃连接不是每秒 400 个请求。连接数设多少取决于被测服务期望的并发连接水位。常见做法是从 100 开始爬坡按 200、400、800、1200 四档往上加每档跑 30 秒记录 QPS 和延迟变化。连接数偏低时 QPS 会随连接数线性上涨涨到某个点后 QPS 不再明显增加、延迟却开始抬升这个拐点就是被测服务当前的连接水位。直接-c8000开局不是不行但结果往往是被测服务的 accept 队列先被打满压测结论变成「服务扛不住」而实际是参数设置不合理。这里有一个边界要明确wrk 的连接数是压测机到被测服务之间的并发连接数不是业务意义上的在线用户数。一个在线用户可能每 5 秒才发一个请求而 wrk 的每个连接都在满速发请求。换算关系大约是「wrk 连接数 预期并发用户数 ÷ 单用户平均请求间隔秒数」压测前先做个粗略换算能省很多解释成本。3.3 时长给足 30 秒以上超时按业务 SLA 设-d控制压测时长单位支持秒和分钟-d30s、-d2m都合法。时长太短结果没有参考价值被测服务的连接池、缓存、JIT 编译都还没热起来压出来的 QPS 偏低而且 10 秒内碰到的异常样本太少延迟分布不稳定。我一般至少压 30 秒做容量评估时压到 2 到 5 分钟。超过 1 分钟的压测能暴露两类慢问题内存泄漏导致的服务崩溃、连接泄漏导致的句柄耗尽。这两类问题在短压里根本看不见。-T是单请求超时时间按业务 SLA 设。比如接口要求 2 秒内返回就加-T2s超过 2 秒的请求会被 wrk 计为超时从延迟统计里单独拿出来。不设-T时wrk 会一直等慢请求返回一个卡死的请求可能拖住整个压测进程让结果出现大面积假延迟。3.4 一组参数对比实验八线程四百连接压到稳定后的读数把参数逻辑落成一组可复现的实验比记住任何推荐值都可靠。以下是我常跑的对比结构压测目标是同一台预发机器上的同一个接口wrk -t8 -c100 -d30s --latency http://target/api/health wrk -t8 -c400 -d30s --latency http://target/api/health wrk -t8 -c800 -d30s --latency http://target/api/health三次压测的唯一变量是连接数其他条件完全一致。运行顺序上推荐从连接数小的开始避免第一次高压把服务打到异常状态污染后续结果。假设实际读数如下示例值用于说明判断方法连接数QPSAvg Latency说明100382002.6 ms连接数不足QPS 没跑满400393009.8 msQPS 接近峰值延迟仍可接受8003910020.5 msQPS 不再增长延迟翻倍已经过水位判断逻辑很直白QPS 从 100 到 400 还在涨说明服务有余量400 到 800 QPS 没涨但延迟翻倍说明瓶颈已经从连接数转到了服务端的处理能力上。400 到 800 之间的某个点就是这台服务的容量水位。记录这三组数据后再决定要不要把线程数翻倍去验证是否压测机自身受限。整个流程走完你对这套参数的把握会远超背几个推荐值的效果。4. 用 Lua 脚本把 wrk 变成接口压测器POST、Token 和随机数据4.1 一个能压 POST 接口的最小 Lua 脚本wrk 自带的默认行为是 GET 请求压 POST 接口必须写 Lua 脚本。脚本本质上是一组回调函数wrk 在合适的时机调用它们。先看一个最简 POST 脚本-- post.lua 最小 POST 压测脚本 local body {page:1,size:20} request function() local headers { [Content-Type] application/json } return wrk.format(POST, /api/order/list, headers, body) end调用方式wrk -t8 -c400 -d30s -s post.lua --latency http://target脚本里request函数每次发请求前被调用返回一个完整的 HTTP 请求字符串。wrk.format是构造这个字符串的便捷方法四个参数分别是方法、路径、请求头、请求体均可省略省略时沿用 wrk 全局表里的默认值。压测时的 URL 写在命令行末尾脚本里只写路径这样换环境压测不用改脚本。这段脚本里每个请求的 body 都是同一个字符串压无状态的查询接口没问题但压写接口时所有请求数据完全一样服务端可能命中缓存也可能触发幂等去重。想要更真实一些就得在 request 里动态生成请求体这就是 4.3 的内容。4.2 带 Token 和签名的请求先登录拿票据再压业务接口压需要登录态的接口时最容易犯的错是在业务压测里连登录接口一起压。登录接口通常有验证码、频率限制、密码哈希计算它自己就是瓶颈混在一起压测业务接口的真实水位会被登录接口拖垮。正确做法是先把 Token 准备好再用脚本里的 init 回调加载进去。Token 可以先用 curl 模拟登录拿一次写入文件脚本启动时读取-- token.lua 读取预生成的 token 并附加到请求头 local token function init() local f io.open(token.txt, r) token f:read(*l) f:close() end request function() local headers { [Authorization] Bearer .. token, [Accept] application/json } local path /api/user/profile?ts .. os.time() return wrk.format(GET, path, headers) endinit函数在每个 worker 线程启动时执行一次适合做文件读取、预计算这类初始化操作。token.txt里只有一行 token 字符串多个线程读同一个文件不会出现每个线程各自登录把登录接口打挂的问题。如果接口要求签名通常是把时间戳、随机数和请求体拼起来做哈希。这类计算放在 request 函数里没问题但要注意签名计算消耗的是压测机的 CPU 和被测服务的 CPU如果签名算法太重压测结果反映的可能是签名开销而不是业务接口能力。遇到这种情况我一般先压一个没有签名的旁路接口做基线再压带签名的接口两个结果一减签名开销就出来了。4.3 随机数据与响应码统计request 和 response 两个回调就够了业务压测需要模拟不同用户、不同参数的请求wrk 提供了math.random但要注意它生成的随机数在高并发下可能不够均匀。更常用的方案是维护一个自增计数器拼参数保证每次请求的 ID 不同-- order.lua 动态请求体 响应码统计 local counter 0 request function() counter counter 1 local user_id 1000000 (counter % 999999) local body string.format({user_id: %d, seq: %d}, user_id, counter) local headers { [Content-Type] application/json } return wrk.format(POST, /api/order/create, headers, body) end这段脚本用计数器自增构造 user_id既能保证请求体动态变化又避免了math.random在极端情况下的碰撞问题。string.format做字符串拼接比..连接在大量调用时更高效压测脚本里这微小的性能差异也会被放大。只看 QPS 不够还需要知道响应码分布。加上 response 回调local count_2xx 0 local count_5xx 0 response function(status, headers, body) if status 200 and status 300 then count_2xx count_2xx 1 elseif status 500 then count_5xx count_5xx 1 end end done function(summary, latency, requests) io.write(2xx count: .. count_2xx .. \n) io.write(5xx count: .. count_5xx .. \n) io.write(Socket errors: .. summary.errors .. \n) endresponse 回调在每个响应返回时触发三个参数是状态码、响应头、响应体。注意不要在 response 里直接print压测期间打印每个请求会严重拖慢 wrk 自身性能正确姿势是把计数累加到局部变量最后在 done 回调里统一输出。done 在压测结束时执行一次summary 里带了请求总数、错误数、耗时统计这些数据加上自己统计的 2xx/5xx 分布就能完整判断接口在压力下的健康度。5. wrk 压测避坑句柄数、线程膨胀、结果失真这几个雷我替你踩过5.1 too many open files先查 ulimit再查被测服务现象wrk 跑起来后 QPS 远低于预期输出里 Socket errors 的 connect 计数持续增长或者直接报too many open files。原因Linux 默认的单进程句柄数限制是 1024wrk 打开 400 个连接需要 400 个文件描述符还没算上标准输入输出和内部管道很容易撞上限。这个限制在压测机上跟被测服务关系不大。解决压测前临时放开当前 shell 的句柄限制ulimit -n 102400 wrk -t8 -c400 -d30s http://targetulimit -n 102400只会影响当前 shell 和它的子进程关掉终端就失效不需要改系统配置。如果 wrk 是跑在 systemd 服务里的还需要在 service 文件里配 LimitNOFILE 并 reload临时 ulimit 对它不起作用。检查是否生效先执行ulimit -n看输出再跑压测这步操作 10 秒能省半小时排查时间。5.2 线程翻倍 QPS 不涨反跌压测机比被测服务先撑不住现象-t8压出 38000 QPS把参数改成-t16QPS 反而掉到 35000延迟还变高了。原因线程数超过 CPU 核数后压测机自身的线程调度开始抢占 CPU每个线程的 epoll 事件处理被频繁打断。wrk 各线程之间还有共享统计计数器的原子操作线程越多锁竞争越明显。这几项开销叠加压测机变成了瓶颈被测服务的真实水位被掩盖了。解决用top看压测机 CPU 使用率如果 wrk 进程的多个线程已经吃满所有核先降线程数而不是继续加。回到 3.1 的原则-t取核数或核数的 1.5 倍封顶。想排除压测机限制可以加一台压测机分担流量对比两台机器的结果是否一致。血泪经验是先跑一次小连接数基线确认压测机 CPU 有余量再上大规模参数否则翻车了都不知道是压测机还是被测服务的问题。5.3 全超时但 curl 正常accept 队列被打满现象wrk 里大量请求的延迟显示为超时但用 curl 单独访问接口是正常的甚至很快。原因被测服务进程的 accept 队列满了新连接在内核态排队等待被 accept应用层根本没收到请求。wrk 的高连接数建连速度快于被测服务的 accept 速度时就会这样服务端内核参数net.core.somaxconn默认值 128 是常见元凶。解决先确认现象用ss -lnt看监听端口的 Recv-Q 列ss -lnt | grep :8080Recv-Q 持续堆积说明 accept 队列被打满。然后调被测机器的内核参数sysctl -w net.core.somaxconn65535 sysctl -w net.ipv4.tcp_max_syn_backlog65535sysctl -w是临时生效重启后失效生产环境需要写/etc/sysctl.conf持久化。注意这个参数要改在被测服务的机器上不是压测机。还有一层如果被测服务是 Java 的 Tomcat 或 Netty应用层有自己的 accept 计数内核队列调大之后应用层也可能需要同步调整Tomcat 的acceptCount和 Netty 的SO_BACKLOG都要看一眼改完一处不管另一处问题照样复现。5.4 结果方差大到没法用区分被测服务抖动和压测工具抖动现象同一接口连续压三次QPS 波动超过 10%延迟的 Stdev 数值甚至比 Avg 还大。原因两种可能。被测服务存在偶发抖动比如 GC 停顿、慢查询、定时任务抢 CPU或者压测机自身不稳定比如共享机器上其他进程占用了 CPU 和带宽。wrk 的单机模式对压测机环境很敏感同机跑个编译任务压测数字立刻变形。解决压测机上用pidstat -p wrk_pid 1观察压测进程的 CPU 使用是否稳定同时用sar -n DEV 1看网卡流量有没有异常波动。被测服务这边看 GC 日志或慢查询日志。一个实用技巧是先压被测服务的健康接口再压业务接口健康接口方差小但业务接口方差大问题大概率在业务代码的偶发开销上两个接口方差都大先检查压测机环境。这块排查最玄学但按「压测机 → 网络 → 被测服务」的顺序查基本能定位。5.5 压测机 CPU 先冲到 100%这个 QPS 只能当参考现象压测机上top显示 wrk 进程 CPU 使用率长期 100%QPS 不再随连接数增长或者增长非常缓慢。原因wrk 虽然是高效的单机压测工具但也有上限。特别在 HTTPS 压测场景TLS 握手和加密解密消耗大量 CPU或者 Lua 脚本里有复杂的字符串拼接和正则每请求的开销过大压测机先于被测服务到瓶颈。解决先看压测机 CPU 是不是被加密计算占满HTTPS 场景可以考虑在压测机和服务之间跑 HTTP 明文做对比排除 TLS 开销。脚本里避免正则和大量字符串操作能用string.format就不用..循环拼接。如果压测机已经到极限而服务端还很轻松加一台压测机做分布式压测是正路但 wrk 原版没有分布式能力需要自己写调度脚本分发任务再汇总结果或者换用支持分布式的压测平台。单机 wrk 跑出的数字在压测机 CPU 打满时只能当参考不能作为容量决策依据。6. 压完怎么验证结果可信再进阶做阶梯加压6.1 用系统侧指标交叉验证 wrk 的数字wrk 自己报的 QPS 和延迟是客户端视角还需要从服务端视角交叉验证。服务端是 Nginx 就看 access log 里的$request_time是 Java 就看 GC 日志和应用监控。一个简单办法是压测结束后用 awk 统计 access log 的耗时百分位awk {print $NF} access.log | sort -n | \ awk {a[NR]$1} END {print p50a[int(NR*0.50)] p99a[int(NR*0.99)]}$NF 字段取的是 access log 每行最后一个字段需要确认你的 log_format 把 request_time 放在末尾。对比 wrk 输出的延迟分布和 access log 统计的延迟分布两者接近说明压测流量真实到达了服务端wrk 的数字可信差别大说明有环节出了问题检查 wrk 的 Socket errors。同一时间看一眼ss -lnt里的连接数确认压测期间的并发连接数和 wrk 的-c参数一致。6.2 阶梯加压记录每一档读数找到服务水位线按 3.2 的方法跑阶梯加压后把数据落成一张模板表方便横向对比连接数QPSAvg LatencyP99压测机 CPU服务端 CPU错误数200265007.5 ms18 ms30%42%0400388009.8 ms25 ms55%78%08003910020.5 ms62 ms85%96%3水位线的判断规则QPS 停止增长且延迟开始快速抬升说明服务端处理能力已到上限服务端 CPU 接近 100% 而 QPS 没涨说明瓶颈在 CPU。有了这张表后续做容量规划就可以直接说「这个接口 400 连接以内是安全区超过 800 会劣化」而不是给一个孤零零的 QPS 数字。6.3 把实验现场写成记录留作后悔药压测结论必须附带当时完整上下文否则两周后回看数字就是黑匣子。我自己会固定记录wrk 版本、完整命令、wrk 二进制来源、压测机核数和内核参数、被测服务版本和配置、压测时间窗口。这些信息缺一条复盘时就得靠猜而压测最怕的就是拿一次无法复现的结果去拍容量决策。养成这个习惯后你再看任何压测报告都会先问参数和现场而不急着看结论。希望帮到你。本文还有配套的精品资源点击获取