wrk压测工具部署与实战:从已编译包到业务级压测
简介一份已编译的wrk HTTP压测工具包专为需要评估Web服务器、API接口或负载均衡器性能的开发、测试与运维人员准备。wrk基于LuaJIT脚本支持通过自定义脚本模拟请求模式、校验响应状态、控制请求速率能够灵活构造高并发测试场景。压缩包约214.16MB内含可直接运行的wrk可执行文件与运行所需依赖解压后无需编译即可通过命令行启动压力测试大幅降低工具部署门槛。该资源已有258人学习其中涵盖wrk基本参数说明、Lua脚本示例以及Requests/sec、Transfer rate、Latency等关键性能指标解读可帮助使用者快速理解测试输出并定位服务瓶颈。借助这套工具包既能进行短时并发压测也能编写复杂测试逻辑是日常性能调优与容量评估的实用助手。1. 拿到 wrk.tar.gz 压测工具先别急着跑这份“已编译”的包到底能省多少事同事分过来一份wrk.tar.gz压测工具包压缩包标注写着“已经编译完”解压后确实能看到 wrk 可执行文件。但真到压测时HTTPS 接口握手失败、连续三轮吞吐差 30%、连接数一上千就直接报错这些问题大多跟编译本身没关系而是出在部署环境、参数设置和结果解读上。这篇笔记只围绕一个场景手里已经有编译好的 wrk 包怎么把它变成一台稳定、可信的压测机。覆盖四件事解压部署、参数定标、报告解读、用脚本压真实业务。适合后端开发、运维和测试开发尤其是被 JMeter 内存问题折腾过、想换轻量级压测工具的团队。2. 部署编译好的 wrk 包解压、依赖检查与第一条可用命令2.1 解压前先看包结构tar tzf 决定安装目录从共享盘或 CI 产物目录拿到wrk.tar.gz后最常见的翻车方式就是直接tar -zxvf解压到当前目录然后到处找 wrk 二进制。tar 包可能打包了完整目录结构也可能只有单个可执行文件解压前先预览内容再决定装到哪。tar tzf wrk.tar.gz # 输出可能是单个文件wrk # 也可能带目录层级wrk/ wrk/wrk wrk/scripts/ mkdir -p /opt/wrk tar xzf wrk.tar.gz -C /opt/wrk ls -l /opt/wrk/wrktar tzf预览内容是解压前必做的一步尤其当包是从其他工程师编译机上直接打的有可能把编译目录一起打进去释放后找不到二进制。后面三条命令把内容解开到独立目录/opt/wrk不让它污染/usr/local/bin。-C参数指定目标目录比先cd再解压更干净。我一般把 wrk 装在/opt/wrk而不是/usr/local/bin原因很实际wrk 是性能压测工具不是常驻服务装进系统目录反而不利于多版本管理。如果以后想对比 wrk 原版和 wrk2 的结果各自放一个目录切换软链就行。还要确认二进制是否带可执行权限ls -l输出里x权限位缺失就chmod x补上。这个检查几秒钟就能避免后面出现Permission denied。2.2 用 ldd 检查动态库依赖glibc 和 libssl 最容易翻车“已经编译完”只代表在编译机上能跑目标机器可能缺少对应的动态库。wrk 依赖 libc、libssl部分发行版的打包方式还会依赖 libluajit。把包拷到目标机后第一件事是跑ldd。ldd /opt/wrk/wrk # 期望输出类似 # libc.so.6 /lib64/libc.so.6 # libssl.so.3 /lib64/libssl.so.3 # libluajit-5.1.so.2 not foundldd列出每个动态库的解析路径not found是最直接信号目标机没有这个库。libluajit 缺失很常见因为很多发行版不自带 LuaJIT需要额外安装或者让编译的人出静态链接版本。libssl 版本不一致时不会直接显示not found而是解析到不同版本的 so 文件这类问题通常要压 HTTPS 接口时才暴露。glibc 的问题更隐蔽。wrk 在较新系统上编译链接的 glibc 符号版本如果高于目标机运行时会直接报version GLIBC_2.34 not found。检查目标机 glibc 版本可以用下面这条命令。ldd --version | head -1 # 编译机是 2.34目标机是 2.17这个二进制基本跑不动遇到 glibc 版本不匹配不要想着补库glibc 是系统最底层组件不能随便替换。常见做法是让编译的人在相近发行版上重新编译或者改用全静态版本。判断“能不能用”的时间点应该放在解压后而不是压测现场。2.3 放入 PATH 并做版本验证wrk -v 只证明能启动依赖检查过后把 wrk 放进默认搜索路径然后做一次最小的启动验证。ln -sf /opt/wrk/wrk /usr/local/bin/wrk wrk -v # 能输出 Usage 或版本信息说明动态库装载链路 OK软链放到/usr/local/bin是因为这个目录通常在普通用户的 PATH 里之后直接敲wrk就能调用不需要每次带全路径。wrk -v这一步可以理解为一次“冒烟测试”它能启动说明ldd阶段没有致命问题。但wrk -v只能证明“能启动”不能证明“能压测”。下一步必须用一个最小命令跑通真实请求。我习惯用本地服务的健康检查接口比如http://127.0.0.1:8080/ping10 秒、50 个连接跑一遍确认有完整输出再进入参数调优阶段避免一开始就在复杂场景里排错。3. 用 wrk 跑通第一次压测线程、连接、时长三个参数怎么定3.1 线程数 -t 为什么不能超过 CPU 核数wrk 的核心模型是每个线程跑一个 epoll 事件循环用非阻塞 I/O 复用大量连接。它跟 JMeter“一个线程模拟一个用户”的模型完全不同并发能力不靠线程堆出来。所以-t不是越大越好线程数超过 CPU 核数后每个线程抢不到调度上下文切换反而吃掉压测机自身的性能。先讲清选型理由为什么团队普遍用 wrk 而不是 JMeter 或 ab常见原因是 wrk 适合在 Linux 上做高并发压测单机就能制造较大连接压力内存占用低没有 GUI 开销。ab 简单但对连接复用支持弱高并发下压测机 CPU 容易先被打满JMeter 在 Windows 上跑高并发容易碰线程和内存瓶颈脚本维护也重。工具线程模型适合场景常见痛点ab单进程多连接简单 GET 压测连接复用弱高并发下 CPU 高JMeter线程模拟用户复杂业务流内存占用高脚本维护重wrkepoll 多线程高并发吞吐压测没有 GUI需要脚本补业务wrk 使用者真正在意的不是“模拟多少用户”而是“这台压测机能不能压出足够的请求压力”。因此-t的默认建议很简单用nproc看核数核数是多少就填多少不要盲目翻倍。3.2 连接数 -c 和时长 -d先短后长多轮取中位数-c控制的是“同时保持的 HTTP 连接数”每个线程管理的连接数是总连接数除以线程数。连接数太大时服务端连接队列、文件描述符、TIME_WAIT 都会成为瓶颈压出来的数字反映的是系统负担而不是业务真实吞吐。压测目标-t-c-d第一次冒烟CPU 核数505s常规吞吐压测CPU 核数20030s长稳验证CPU 核数200300s 或更长-d的单位不能省10s、1m都是合法写法。时长太短连接建立和 JIT 预热还没稳定就结束了太长又会引入定时任务和 GC 噪声。完整测试周期内我一般连续压三轮取中间值而不是最大值最大值最容易撞上偶然波动。连接数的调整逻辑是一个循环判断如果 Latency 的 P99 随连接数加大明显上涨说明服务端处理能力接近上限加连接数只会让排队更难如果 Req/Sec 还在涨说明压力还没打满可以继续加。这个“看延迟、调连接、看吞吐”的闭环就是 wrk 调参的核心。3.3 最小可用命令与输出快读一条命令看清五块信息wrk -t4 -c100 -d10s -L http://127.0.0.1:8080/ping # -t 线程数-c 连接数-d 时长-L 打印延迟分布提示-t 和 -c 建议保持整数倍关系连接数远小于线程数时压不出有意义的数据。跑通后看五块信息Running 10s test本次压测的时长和地址。Thread Stats每个线程自己的统计。Latency延迟平均值、标准差、最大值、正负一个标准差覆盖的比例。Req/Sec每个线程每秒完成的请求数。Requests/sec全量吞吐对外汇报最常引用这个数字。Transfer/sec每秒传输量判断带宽瓶颈时用。Latency Distribution只有加-L才出现按 50%、75%、90%、99% 分位展示。第一次跑通以后把完整输出保存下来作为基准。wrk -t4 -c100 -d10s -L http://127.0.0.1:8080/ping | tee /tmp/wrk_base.txttee把终端输出同时写进文件后面每次调参重跑一遍用diff或肉眼对比就知道改参数带来了什么变化。没有基准的压测报告很难说服别人这个习惯建议从一开始就养成。4. 读懂 wrk 压测报告Latency、Req/Sec、Transfer 与 Socket errors4.1 Latency 分布要加 -L 才完整P99 比平均延迟更值得盯wrk 默认输出的 Latency 只有平均值、标准差和最大值。平均值在性能调优里参考价值有限一个接口平均 10ms但 P99 是 200ms用户体验早就被尾部延迟拖垮了。想看分位数必须加-L。判断规则很简单Avg 和 P99 接近说明服务稳定P99 是 Avg 的三倍以上说明存在长尾常见原因是线程池排队、锁竞争、慢 SQL。Max 远大于 P99说明偶发大停顿通常和 GC、定时任务相关。遇到这种情况不要只盯 Req/Sec先查服务端监控里的 GC 时间和线程池队列长度。报告特征可能问题下一步Avg 与 P99 接近服务处理稳定增加 -c 看是否还有余量P99 明显大于 Avg长尾延迟查线程池排队、锁竞争、慢 SQLMax 远大于 P99偶发停顿查 GC、定时任务、磁盘抖动4.2 Req/Sec 和 Transfer/sec带宽是不是瓶颈一算便知Requests/sec是所有线程完成的请求总数除以总时长这是对外汇报的核心吞吐。Transfer/sec是这段时间内读到的字节数。两者结合能判断瓶颈位置如果 Transfer/sec 已经接近网卡速率上限说明压测结果受带宽限制不是服务真实吞吐。举例4 线程 100 连接压一个响应体约 1KB 的接口得到 Req/Sec 12000、Transfer/sec 12MB/s。看起来吞吐很高但如果压测机是千兆网卡这个带宽已经接近上限接下来要做的是用小响应体接口或更小并发确认服务本身还有没有余量。awk /Requests\/sec:/ {print Req/s:, $2} /Transfer\/sec:/ {print Transfer:, $2} /tmp/wrk_base.txt # 把基准报告里的两个关键数字一次提取出来awk用正则匹配行$2是数字列。配合 tee 保存的报告改参数后重跑一次两个文件一对比就知道吞吐变化。提取时注意行首格式要严格匹配如果报告字段有变化先grep看实际行内容再调整awk。4.3 Socket errors 三个计数分别代表什么连接失败、读中断、写失败wrk 结束后可能出现一段Socket errors格式是connect, read, write, timeout。正常情况下应该全是 0 或数量很少。如果错误占比超过千分之一这份压测结果基本不能用。connect建连失败。目标机 accept 队列满或压测机连接数超过限制。先把-c调小再看服务端 backlog。read读响应中断。常见于 keep-alive 连接被服务端关闭而 wrk 还在等数据。每秒大量出现说明服务端连接生命周期策略和 wrk 不匹配。write写请求失败。经常和 read 成对出现。timeout单个请求超过 wrk 等待上限配合-L的分位数看最直观。遇到 Socket errors 不要急着怀疑 wrk。先看压测机和目标机的连接状态用ss -s统计 TIME_WAIT 数量。如果几万个说明连接没有正常复用回去查服务端 keep-alive 超时配置。wrk 默认会复用连接但服务端主动断开后它会重建这本身就会带出 connect 错误。5. wrk 压测常见的 5 个坑现象、原因与排查方法5.1 HTTPS 接口压测失败Protocol wrong number现象压 HTTP 接口一切正常压 HTTPS 接口报错或一直连接失败错误信息类似Protocol wrong version number或其它 TLS 握手失败。原因wrk 编译时链接的 OpenSSL 版本与目标机的 libssl 不一致。目标机可能是 libssl3 而 wrk 是拿 libssl1.1 编的或者反过来。解决先用ldd /opt/wrk/wrk | grep ssl确认当前链接的是哪个版本再用 HTTP 接口验证排除 TLS 层问题。如果必须压 HTTPS要么补齐一致版本的 libssl要么让编译的人用匹配的openssl-devel重新编译。这里最忌讳的是在压测现场临时换 so 文件会把整个压测环境搞乱。5.2 编译完的包在目标机跑不起来glibc 版本不匹配现象目标机执行 wrk 提示version GLIBC_2.34 not found或者直接段错误。原因编译机系统较新动态链接了高版本 glibc 符号目标机旧系统没有。这是“编译完”三个字最容易误导人的地方编译环境与运行环境不一致二进制就不能直接搬。解决两侧分别跑ldd --version对比不要尝试替换目标机 libc让编译的人在相近版本发行版上重新编译或者改用静态编译版。判断时间点应放在部署阶段而不是压测现场。5.3 URL 里的 符号被 shell 吃掉请求数对不上现象压测带 query string 的 GET 接口时wrk 实际发出的请求数远小于预期或者 shell 把部分 URL 当成后台任务执行。原因URL 里在 bash 中是后台执行符号/api?page1size10会被拆成两条命令。解决整个 URL 用单引号包起来。wrk -c100 -d10s http://127.0.0.1:8080/api?page1size10如果脚本里还有$、括号等特殊字符也要注意转义。这个坑不大但一旦压测中途发现请求数不对浪费的时间比想象中多。5.4 压测机 CPU 打满而目标机很闲wrk 自己成了瓶颈现象top 里看到 wrk 进程占满所有 CPU目标机 CPU 占用却不到 30%QPS 也上不去。原因压测机到了极限。常见是-t超过核数或者 Lua 脚本里每次请求都做昂贵的字符串拼接、随机数生成。最常见做法是把string.format放进request()里高频调用压测机 CPU 全耗在格式化上。解决先把-t降到 CPU 核数重测脚本里公共 body 提前拼好不要在请求函数里重复计算仍打不满就加压测机数量或者改用固定 QPS 模式验证目标机真实容量。single 压测机无法打满目标服务时强行加大-c只会让误差更大。5.5 连接数一大就报错ulimit -n 限制撞上了现象-c从 200 加到 1000wrk 直接报Too many open files或者大量 connect 错误。原因当前 shell 的ulimit -n默认 1024压测机能打开的 socket 数量不够。一个连接至少占一个文件描述符1000 个连接加标准输入输出、日志文件轻松超过默认值。解决ulimit -n 65535这个命令只对当前会话临时生效。要长期生效写入/etc/security/limits.conf。注意非 root 用户的限制在重新登录时可能被重置改完配置需要注销重进。压测机上把 nofile 调大是标准前置动作和 wrk 本身无关但最容易被忽略。6. 用 Lua 脚本把 wrk 从“压 URL”变成“压业务”POST、鉴权与结果校验6.1 最小 POST 脚本改三个全局量就能压接口wrk 自带的 LuaJIT 接口并不复杂最常见的 POST 压测只需要设置三个全局量。-- post.lua 最小 POST 压测脚本 wrk.method POST wrk.headers[Content-Type] application/json wrk.body {page:1,size:10}wrk -t4 -c100 -d10s -s post.lua http://127.0.0.1:8080/api/list-s指定脚本路径。脚本顶层设置的wrk.method、wrk.headers、wrk.body会作用到所有请求适合请求体固定的场景。注意 header 的 key 大小写Content-Type写错服务端可能直接返回 415。6.2 动态参数与响应计数request 和 response 的配合真实业务几乎都要动态参数和鉴权这时需要用request()函数在每次请求前生成内容再用response()统计业务状态码。-- dynamic.lua 动态参数 响应校验 local ok, fail 0, 0 function request() local body string.format({uid:%d,ts:%d}, math.random(10000, 99999), os.time()) return wrk.format(POST, /api/order, {[X-Token]abc123}, body) end function response(status, headers, body) if status 200 then ok ok 1 else fail fail 1 end end function done(summary, latency, requests) io.write(string.format(ok%d fail%d total%d\n, ok, fail, summary.requests)) endwrk.format四个参数分别是方法、路径、header 表、body返回完整请求字符串。math.random在线程内独立多线程下不需要加锁。response()在每个响应到达时触发这里只按状态码计数实际项目里可以在fail分支里记录 body 内容用于事后排查。6.3 先小后大用 3 秒小压测给正式结果“探路”任何脚本改完之后先用-c10 -d3s跑一遍看 done 里的 ok/fail 计数是否合理。wrk 报告的Requests/sec是“请求发出数”ok fail才是“业务响应数”两者相差过大说明有请求在连接层就断了脚本或鉴权 header 有问题。确认没有 5xx、没有连接错误后再逐步加并发。想固定 QPS 验证容量时可以换 wrk2 的-R参数指定每秒请求数输出比自由模式更稳定。我在这里吃过亏脚本里一次不起眼的string.format在 10 万 QPS 下会放大成几百倍的 CPU 开销现在都先把公共 body 模板拼好再考虑并发参数。希望你拿到这份 wrk 包后能少走这段弯路希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →