4. wrk实战
好,咱们进入实战环节。前面聊了那么多理论,现在该动手了。这一章我带你玩转 wrk,一个轻量级的 HTTP 压测工具。说实话,我用了这么多年压测工具,wrk 是我个人最常用的之一——因为它够快、够简单,几行命令就能把服务的底裤扒干净。
4.1 wrk 安装:别踩这些坑
先说说安装。wrk 的安装其实很简单,但我在项目中遇到过几次因为环境问题折腾半天的情况。嗯,这里要注意几个点。
Linux 环境安装
# 从源码编译安装(推荐)
git clone https://github.com/wg/wrk.git
cd wrk
make
# 安装到系统路径
sudo cp wrk /usr/local/bin/
# 验证安装
wrk --version
你可能会问:为什么不用 apt-get 直接装?我建议你尽量用源码编译。为什么?因为系统源里的 wrk 版本往往比较老,有些新特性不支持。我记得有一次在 CentOS 7 上用 yum 安装,结果版本是 4.0.1,连 HTTP/2 都不支持,白忙活半天。
sudo apt-get install libssl-dev luajit(Ubuntu)或 sudo yum install openssl-devel luajit(CentOS)。
macOS 环境安装
brew install wrk
macOS 用户就简单了,Homebrew 一把梭。不过要注意,M1/M2 芯片的 Mac 可能会遇到一些兼容性问题,建议用 Rosetta 2 模式运行。
4.2 核心参数:-t, -c, -d 到底怎么用?
wrk 的参数不多,但每个都很关键。说白了,你只要搞懂这三个参数,就能应付 90% 的场景。
| 参数 | 全称 | 含义 | 我的建议 |
|---|---|---|---|
| -t | threads | 压测线程数 | 一般设为 CPU 核心数的 2-4 倍 |
| -c | connections | 保持的连接数 | 从 100 开始,逐步增加 |
| -d | duration | 压测持续时间 | 至少 30 秒,推荐 60 秒 |
-t 线程数:你想想看,线程数不是越多越好。我见过有人一上来就开 64 个线程,结果 CPU 上下文切换开销比压测本身还大。我个人习惯是:先看服务器 CPU 核心数,比如 8 核,我就设 -t 16。为什么?因为 wrk 的线程是 I/O 密集型的,多开一点能充分利用 CPU。
-c 连接数:这个参数模拟的是并发连接数。注意,它不等于 QPS。我曾经犯过一个错误:以为 -c 越大 QPS 越高。结果发现当连接数超过某个阈值后,QPS 反而下降了——因为连接管理本身也有开销。建议从 -c 100 开始,每次翻倍,直到找到拐点。
-d 持续时间:压测时间太短没有意义。为什么?因为 JVM 有预热、连接池有初始化、GC 有抖动。我一般至少跑 60 秒,前 10 秒的数据直接丢弃不看。你想想看,如果只跑 10 秒,可能刚好碰到一次 Full GC,结果就失真了。
4.3 实战:压测一个 HTTP 服务
好,理论说完了,咱们直接上手。假设我要压测一个本地的 Spring Boot 服务,端口是 8080,接口是 /api/hello。
# 基础压测命令
wrk -t 12 -c 400 -d 30s http://localhost:8080/api/hello
这条命令的意思是:用 12 个线程,保持 400 个并发连接,持续压测 30 秒。跑完之后,你会看到类似这样的输出:
Running 30s test @ http://localhost:8080/api/hello
12 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 45.67ms 12.34ms 256.78ms 75.23%
Req/Sec 723.45 89.12 1.02k 68.90%
259842 requests in 30.00s, 34.56MB read
Socket errors: connect 0, read 0, write 0, timeout 0
Requests/sec: 8661.40
Transfer/sec: 1.15MB
这个结果怎么看?我带你逐行解读。
4.4 结果解读:别只看 QPS
第一行:测试概要
告诉你跑了多久、压的哪个地址、用了多少线程和连接。这里有个小细节:如果压测时间比设定的短,说明服务扛不住提前挂了。我曾经遇到过压了 10 秒服务就 OOM 的情况,结果直接报错退出。
Thread Stats 部分:延迟分布
- Latency Avg:平均延迟 45.67ms。这个值越低越好。
- Latency Stdev:标准差 12.34ms。如果这个值很大,说明延迟波动厉害,服务不稳定。
- Latency Max:最大延迟 256.78ms。这个值很关键——如果最大延迟是平均延迟的 5 倍以上,说明有长尾请求。
- +/- Stdev:75.23% 的请求在标准差范围内。这个比例越高,说明延迟越集中。
--latency 参数看百分位延迟。
Req/Sec 部分:吞吐量
- Avg:每个线程每秒处理 723.45 个请求。
- Max:峰值达到 1.02k。这个值可以帮你判断服务的极限在哪里。
总请求数和数据量
30 秒内处理了 259842 个请求,读取了 34.56MB 数据。这里要注意:如果 read 的数据量远小于预期,说明响应体可能被截断了或者服务返回了错误。
Socket errors
这个部分很重要。如果 connect 或 timeout 不为 0,说明服务端连接池满了或者处理不过来。我遇到过最夸张的一次,timeout 达到了 5000+,一看日志,数据库连接池爆了。
Requests/sec 和 Transfer/sec
最终的两个核心指标:QPS 是 8661.40,吞吐量是 1.15MB/s。这两个值就是你的服务在当前配置下的性能基准。
4.5 进阶技巧:加参数看更多细节
基础用法学会了,我再分享几个我常用的参数组合。
# 看延迟分布(推荐)
wrk -t 12 -c 400 -d 30s --latency http://localhost:8080/api/hello
# 输出结果到文件
wrk -t 12 -c 400 -d 30s --latency http://localhost:8080/api/hello > result.txt
# 使用 Lua 脚本模拟复杂场景
wrk -t 12 -c 400 -d 30s --script=post.lua http://localhost:8080/api/submit
--latency 参数会额外输出延迟的百分位数据,比如 P50、P75、P90、P99。我个人习惯是:P99 延迟不能超过平均延迟的 3 倍,否则就要优化。
4.6 避坑指南
最后,我总结几个实战中容易踩的坑。
- 不要在本机压本机:我曾经犯过这个错,结果压出来的 QPS 高得离谱,以为是服务优化得好,后来发现是网络延迟为 0 导致的假象。建议至少用两台机器,或者用 Docker 隔离。
- 注意 ulimit 限制:Linux 默认的文件描述符限制是 1024,如果你开 400 个连接,每个连接可能占用多个 fd,很容易超限。记得先执行
ulimit -n 65535。 - 压测时间别太短:至少 30 秒,推荐 60 秒。我见过有人压 5 秒就下结论,结果服务刚启动还在预热,数据完全不可信。
- 关注错误率:如果 QPS 很高但错误率也高,那这个 QPS 没有意义。我一般要求错误率低于 0.1%。
好了,wrk 的实战就讲到这里。你拿着这些参数去压一下自己的服务,看看结果是不是符合预期。记住,压测不是为了得到一个好看的数字,而是为了找到服务的瓶颈在哪里。