简介:wrk-4.1.0-linux 是一款专为 Linux 环境设计的高性能 HTTP 基准测试工具,面向后端开发、运维及性能测试人员,用于高并发场景下 Web 服务器的压力测试与性能瓶颈分析,尤其适合短连接、高并发的服务验证。压缩包采用 7z 格式,整体约 168.72MB,文件数量未单独标注;内容以 C 语言源码、Lua 扩展脚本、编译配置及使用说明文档为主,解压后可按文档命令完成编译、权限设置与环境准备。已有 586 人学习/下载。内容系统讲解了 wrk 的安装流程、常用参数(如并发连接数、线程数、测试时长)、自定义请求头及 Lua 脚本定制复杂请求场景,并演示了通过输出中的总请求数、吞吐量、响应时间等指标评估服务性能;同时对比 JMeter、ab 等工具的特性差异,帮助读者结合场景选择合适工具,快速定位高并发下的性能短板,适合需要搭建轻量级压测环境的开发者参考。
1. 先搞清楚 wrk 是什么:为什么先用它做接口摸底
wrk 是一个跑在 linux 命令行里的 HTTP 基准测试工具,4.1.0 是它流传最广的一个版本号。它和 ab、siege 这类老牌工具最大的差别是:几行命令、几十秒时间,就能把一台机器的接口服务压到接近物理上限,报告里直接给出吞吐、延迟分布和错误计数。对后端开发者、运维和性能测试工程师来说,它是做接口容量摸底、性能回退排查、调优前后对比时最顺手的工具。这篇笔记不绕理论,直接讲怎么在 linux 上装好 wrk 4.1.0、命令参数怎么设、输出怎么读,以及我压测实践中踩过的几个坑。你以后看到「wrk 压测结果忽高忽低」先别急着换工具,多数情况是环境和用法问题。
2. wrk 4.1.0 编译安装:源码方式与首个压测命令
2.1 为什么我坚持源码编译:版本与依赖的真相
大多数 linux 发行版的软件源里都有 wrk 这个包,一行命令就能装完。但我建议你编译 4.1.0 源码,原因有两个:一是软件源里的版本不受你控制,有的发行版会冻结在某个旧版本,行为和你查到的文档对不上;二是 wrk 编译非常简单,没有 configure、没有一堆依赖,出问题一般也就是缺一个 OpenSSL 开发头文件,自己编译反而最可控。
wrk 4.1.0 运行时依赖 OpenSSL,编译期需要 gcc、make 和 libssl-dev(CentOS 系叫 openssl-devel)。它的源码里内置了 Lua 运行时,所以你不需要单独装任何 Lua 环境,这也是后面能用 Lua 脚本扩展压测场景的原因。下面这组命令在 Debian/Ubuntu 系上直接可跑。
# 更新软件源,把编译工具链一次装齐 sudo apt update sudo apt install -y build-essential libssl-dev git # 进入解压后的 wrk-4.1.0 源码目录 cd ~/src/wrk-4.1.0 # 直接编译,-j$(nproc) 让 make 按 CPU 核数并行执行 make -j$(nproc) # 复制到 PATH 目录,后续直接敲 wrk 就能用 sudo cp wrk /usr/local/bin/wrk # 验证版本 wrk --versionwrk 的 Makefile 不需要 configure 步骤,make 之后目录里会出现一个名为 wrk 的二进制文件,这一套流程和 linux 常用命令的安装习惯一致。cp 到 /usr/local/bin 是推荐做法,如果你的 PATH 里没有这个目录,改成 sudo cp wrk /usr/bin/wrk 即可。编译报错最常见的是找不到 openssl/ssl.h,说明第二步的 libssl-dev 没装上;在 CentOS 上把安装命令换成 yum install -y gcc make openssl-devel,其余步骤完全一致。
还有一个省事技巧:wrk 编译出来是单二进制,没有运行时动态库依赖,我经常在一台机器上编译好,直接把 wrk 文件同步到其他服务器使用,省去每台机器都装工具链的时间。批量运维场景里,这个做法比逐台编译更靠谱。
2.2 跑通第一次压测:最小命令拆解
# 12 线程、200 并发连接、压 30 秒,并打印延迟分布 wrk -t12 -c200 -d30s --latency http://127.0.0.1:8080/-t 是线程数,-c 是并发连接总数,-d 是持续时间,--latency 要求在结束时打印延迟百分位分布。URL 必须是完整的 http:// 或 https:// 开头。注意 wrk 的并发模型里,线程数和连接数是两个独立维度:-c 200 表示总连接数由 12 个线程分摊,平均每个线程维护 16 到 17 个连接。wrk 用非阻塞 I/O 加事件循环,一个线程可以同时维护成百上千个长连接,这一点和 ab 每个进程一个连接的模式完全不同。
一次典型输出长这样:
Running 30s test @ http://127.0.0.1:8080/ 12 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 45.23ms 12.89ms 231.45ms 62.34% Req/Sec 359.45 52.90 512.00 71.12% Latency Distribution 50% 41.78ms 75% 52.10ms 90% 63.02ms 99% 128.44ms 362280 requests in 30.08s, 48.65MB read Requests/sec: 12041.22 Transfer/sec: 1.62MB
第一行是测试参数回顾;Thread Stats 里 Latency 表示所有请求的延迟分布统计,Req/Sec 表示每个线程平均每秒发出的请求数。Latency Distribution 是全部请求的延迟百分位,最后两行才是总吞吐。这是一组典型的 200 连接压本地服务的数据,Requests/sec 大约 1.2 万。
2.3 线程模型与 epoll:为什么 wrk 能压这么高
wrk 每个线程各自创建一个 epoll 实例,把连接注册进去,用事件驱动方式收发请求。所有 socket 都是非阻塞的,线程在事件循环里处理就绪事件,空闲时不占 CPU。这个模型决定了三个实践结论:一是线程数不需要特别大,通常等于物理核数;二是连接数可以远大于线程数,因为连接只消耗文件描述符,不消耗线程栈;三是 keep-alive 长连接对压测结果影响很大,wrk 默认复用连接,发完请求不会立刻断开。
ab 这类工具的并发模型是 fork 进程或单线程多连接,连接一多上下文切换开销就上来了,压测机自己先成为瓶颈。wrk 用事件驱动,能把压测机的 CPU 更多花在真实请求收发上,所以同样的机器配置,wrk 能压出远高于 ab 的吞吐。这也是 linux 系统管理里常说的「压测先压压测机」问题的源头——工具选不对,结果从根上就是错的。
3. 读懂 wrk 输出:三组必调参数与一个增速误区
3.1 输出字段逐个拆解
先看 Thread Stats 里的 Latency 行:Avg 是所有请求的平均延迟,Stdev 是标准差,Max 是最大单次延迟,+/- Stdev 表示有多少比例的请求落在均值加减一个标准差内。这个百分比越高,说明延迟越集中,服务处理节奏越平稳。反过来,如果这个数字很低,说明延迟抖动严重,服务端有间歇性排队或阻塞。
Req/Sec 行统计的是每个线程每秒能发出的请求数,注意它不是总 QPS,总 QPS 要看最下面的 Requests/sec。Latency Distribution 里的 50/75/90/99 分位是判断毛刺的关键:如果 99% 分位是 50ms,Max 却到了 800ms,说明存在少量长尾请求,这时候问题往往不在整体负载,而在服务端的 GC、连接风暴或线程池排队。
Socket errors 这行只在有错误时出现,没有错误时不会显示。它分四类:connect 是连接建立失败,read 是读响应时被对端断开,write 是请求写出失败,timeout 是超过 --timeout 没收到响应。最后两行,requests in 30.08s 是总请求数和总响应字节数,Requests/sec 是整体 QPS,Transfer/sec 是带宽吞吐。
3.2 三组必调参数:先看这张表
| 参数 | 作用 | 典型取值 | 备注 |
|---|---|---|---|
| -t | 线程数 | 物理核数或核数×2 | 不要远超核数 |
| -c | 并发连接数 | 50 起步,逐步加 | 受 ulimit 限制 |
| -d | 压测时长 | 短压 30s,稳定性 5min+ | 支持 s/m/h 单位 |
| --timeout | 单请求超时 | 默认 2 秒 | timeout 错误多时调大 |
| --latency | 打印延迟分布 | 建议加上 | 否则只有汇总统计 |
| -H | 自定义 Header | 按业务需要 | 可重复传多次 |
| -s | Lua 脚本 | 业务压测 | 见第 5 章 |
-c 连接数是最需要小心试出来的参数。我一般从 50 开始,逐个翻倍到 200、500、1000,每次盯住 Requests/sec 和 Latency 99% 分位的变化。连接数增加但 QPS 不再涨,说明服务端已经到瓶颈,再往上加连接只会拉高延迟。压测时长则要看目的:做接口快筛 30 秒够用,做容量评估建议至少 5 分钟,让慢查询、GC、连接老化这些现象有时间暴露出来。
提示:wrk 4.1.0 没有固定 QPS 模式,如果要做限速压测,比如固定压 5000 QPS 而不是压到最大,需要改用 wrk2 或自己用 Lua 控制请求间隔。这个边界很多人踩过,先记住。
3.3 线程数选多少:一个常见的增速误区
很多人以为 -t 越大越好,实际不是。压 CPU 密集接口,比如 JSON 序列化、加解密、模板渲染,线程数超过物理核数后吞吐就不再上涨,甚至回落;压网络密集接口,比如静态资源、网关转发,可以按核数×2 起步。判断方法很简单:先 nproc 看核数,跑两轮对比。
# 第一轮:16 线程 wrk -t16 -c200 -d30s --latency http://127.0.0.1:8080/ # 第二轮:32 线程 wrk -t32 -c200 -d30s --latency http://127.0.0.1:8080/对比两轮 Requests/sec,取高者,不要凭感觉定线程数。我见过不少新手把 -t 调到 64,结果 QPS 只有 -t8 的一半,原因就是线程调度开销超过了并发收益。wrk 线程数对总吞吐的影响是一条抛物线,找到拐点比盲目堆线程重要得多。这个思路和 linux 运维故障案例里查「并发上不去」是同一套逻辑:先确认资源边界,再谈参数调优。
4. wrk 压测避坑指南:四个高频现象与根因
4.1 连接数上不去:ulimit 撞墙
现象:wrk -c 2000 一启动就报 Couldn't open socket 或 Too many open files。
原因:linux 单进程默认文件描述符上限是 1024,2000 个连接加上标准输入输出、epoll 实例等,很快就撞上限。
解决:当前 shell 先执行 ulimit -n 1048576 再跑 wrk。注意 ulimit 只影响当前 shell 和它的子进程,sudo 执行时会把资源限制重置为 root 默认值,所以用了 sudo -i 之后要重新设置 ulimit。长期使用就改 /etc/security/limits.conf,加上 * soft nofile 1048576 和 * hard nofile 1048576,重新登录后生效。改完可以用 ulimit -n 验证。
4.2 本机压本机:结果忽高忽低
现象:同一命令连续跑三次,Requests/sec 可能分别是 12000、9000、13500,波动超过 30%,被压服务端的 CPU 却始终不满。
原因:wrk 和被压服务在同一台机器上抢 CPU,wrk 线程被调度打断,节流抖动直接反映在数字上。另外 loopback 网卡不经过真实网络路径,链路损耗为零,压出来的数据只适合做同机对比,不适合做容量预估。
解决:压测客户端和服务端拆到两台机器。如果一定要本机压,用 taskset 把 wrk 绑到部分核,服务端绑到另一部分核,减少调度互相干扰:
# 绑定 wrk 到 1、3、5、7 四个核,服务端跑在 0、2、4、6 上 taskset -c 1,3,5,7 wrk -t4 -c200 -d30s http://127.0.0.1:8080/taskset 的核编号要以 lscpu -e 输出的实际布局为准,最好把两个进程分配到不同的物理核心上,而不是超线程兄弟核。
4.3 Socket errors 高:分清是服务端还是压测机的问题
现象:报告出现 Socket errors: connect 120, read 45, timeout 30,QPS 还在涨,但业务方说服务端负载并不高。
原因:三类错误指向不同位置。connect 失败说明服务端 accept 队列满了,接不过来;read 错误是连接被对端主动断开,比如 keep-alive 超时或接入层 reset;timeout 是请求发出去后对端在超时时间内没回包,服务端线程池排队或业务存在慢查询。
解决:先看哪类错误占大头再动手。connect 多调大服务端 listen backlog;read 多查接入层和后端之间的连接超时配置;timeout 多要查业务耗时,而不是一味加 wrk 连接数。还有个经验:wrk 的 timeout 默认 2 秒,压业务接口时把 --timeout 调大到 10 秒或 30 秒,能排除「服务端处理超过 2 秒」带来的噪音,让结果更接近真实体验。
4.4 线程数翻倍吞吐没动:CPU 亲和与调度开销
现象:-t8 -c200 和 -t32 -c200 跑下来总 QPS 几乎一样,有时 32 线程反而更低。
原因:CPU 核心数是物理边界,wrk 线程数超过核数后事件循环线程互相抢核,上下文切换开销吃掉收益。更隐蔽的是,服务端如果配置了 CPU 绑定或 cgroup 限额,压测机这边线程再多也压不上去。
解决:压测前用 nproc 和 lscpu 确认物理核数,wrk 线程数设为物理核数。服务部署在容器里时,还要确认容器 CPU 配额,别拿超卖机器的数据当线上容量依据。压测结果是配置预算,不是理论峰值,这个认知能帮你省掉很多无效调参。
5. 进阶用法:用 Lua 脚本把 wrk 变成业务压测工具
5.1 一段能直接用的 POST 脚本
wrk 内置 Lua 运行时,用 -s 指定脚本就能模拟真实业务,不再只是打一个 GET 的静态页面。
-- http_post.lua:模拟带 JSON body 的 POST 请求 wrk.method = "POST" wrk.headers["Content-Type"] = "application/json" function request() -- 每个请求的时间戳不同,避免服务端缓存影响结果 local ts = os.time() wrk.body = string.format('{"user":"tester","ts":%d}', ts) return wrk.format("POST", "/api/submit", wrk.headers, wrk.body) endwrk -t4 -c100 -d60s -s http_post.lua http://127.0.0.1:8080wrk.method 放在脚本顶部,表示全局请求方法;request() 函数在每次请求前被调用,返回的请求内容可以动态变化。wrk.format 的第一个参数是方法,第二个参数是路径,第三个是 headers,第四个是 body。注意动态 body 的字符串拼接不要太重,压测机自己是单线程 CPU 资源,在 request() 里做复杂计算会让压测机先慢下来,测出的数字就不是服务端的真实能力了。
5.2 只统计 2xx 的响应:response 与 done 组合
-- status_check.lua:按状态码统计请求结果 local total = 0 local ok = 0 function response(status, headers, body) total = total + 1 if status == 200 then ok = ok + 1 end end function done(summary, latency, requests) local rate = 0 if total > 0 then rate = ok / total * 100 end io.write(string.format("2xx rate: %.2f%% (%d/%d)\n", rate, ok, total)) endresponse() 每收到一个响应都会调用,你可以在这里按状态码、响应头、响应体关键字做过滤统计;done() 在压测结束时触发,输出自定义结果。这个模板在验证限流、鉴权、灰度策略时特别有用:把期望状态码改成 429 或 403,就能快速确认策略是否按预期生效。
我把这套脚本按 http_post.lua 和 status_check.lua 存成了模板,每次接新接口先用最简命令跑基线,再用 Lua 脚本补业务场景。压测这件事,工具只是第一步,真正花时间的是理解服务端瓶颈在哪。wrk 的好处是它足够透明,输出简单、行为可预期,能让你把精力花在分析上而不是学工具上。希望帮到你。
本文还有配套的精品资源,点击获取