侧边栏壁纸
  • 累计撰写 121 篇文章
  • 累计收到 2 条评论

接口上线前到底能扛多少并发?我用 wrk 做了几轮压力测试,几个关键指标说清楚

2026-5-23 / 0 评论 / 6 阅读
🤖AI摘要
本文探讨了接口上线前的并发承载能力,作者通过使用wrk和hey等压测工具进行压力测试,强调了压测的重要性。文章介绍了不同压测工具的特点,并详细说明了如何通过RPS、延迟和错误率等关键指标评估接口性能。作者还分享了在压测过程中遇到的坑,如ulimit未调整导致压测端崩溃的问题,并提供了相应的解决方案。

做了几年后端,有个道理我是吃了亏才真正记住的:代码跑得通,不等于上线扛得住

去年做一个活动接口,开发环境测得好好的,上线当天直接被打趴。数据库连接池满了,请求排队超时,用户端收到的全是 502。复盘的时候发现——其实压测工具我一直搁在那没认真用过。

后来花了点时间把 wrk 和 hey 学明白,现在每个接口上线前我都会跑一轮压测。这篇文章把这套做法和路上踩的几个坑记一下。


压测工具不少,我试过的几个:

  • ab(Apache Bench):Apache 自带的,用起来简单,但单线程模型,高并发场景不太准。
  • wrk:轻量、多线程、性能好。一个二进制文件搞定,装起来省事。缺点是只支持 HTTP,不支持自定义脚本(除非用 Lua)。
  • hey:Go 写的,来自 Google 团队(Tarekziade),用法和 wrk 差不多,输出更直观,跑 REST API 很顺手。
  • JMeter:功能全,有 GUI,能做复杂场景。但太重了,我平时做快速验证不想开它。

我现在的主力是 wrk + hey,wrk 看原始性能数据,hey 看详细分布和做简单验证。

安装很简单。wrk 编译一下就行:

git clone https://github.com/wg/wrk.git
cd wrk && make
sudo cp wrk /usr/local/bin/

hey 下载二进制直接跑:

curl -fsSL https://hey-release.s3.us-east-2.amazonaws.com/hey_linux_amd64 -o /usr/local/bin/hey
chmod +x /usr/local/bin/hey

先弄清楚几个指标

开始压之前,得知道自己看什么数据。我一开始也是只知道看"能不能跑",后来才慢慢搞清楚该盯哪几个数。

RPS(Requests Per Second)——每秒能处理多少请求。这是最直接的能力指标。同样一个接口,一台机器能扛 5000 RPS 和只能扛 500 RPS,差的不是一星半点。

延迟(Latency)——不是看平均值,是看分位值。p50 表示一半请求在这之内完成,p99 表示 99% 的请求都在这之内。我一直盯着 p99 看,因为最慢的那 1% 才是真正的用户体验瓶颈。

错误率——压测过程中有没有返回非 2xx/3xx 状态码。如果有,说明系统在某个人流量上开始撑不住了。

这几个数据 wrk 和 hey 都会直接打出来,不用自己算。


先拿一个最简单的 GET 接口试试 wrk:

wrk -t4 -c100 -d30s http://localhost:8080/api/health

意思是:4 个线程,模拟 100 个并发连接,持续压 30 秒。

输出大概长这样:

Running 30s test @ http://localhost:8080/api/health
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    12.34ms    8.56ms 189.27ms   82.33%
    Req/Sec     2.15k   356.45     3.92k    70.12%
  258792 requests in 30.09s, 37.85MB read
Requests/sec:   8600.45
Transfer/sec:      1.26MB

看到 8600 RPS,心里就有底了。如果压出来只有几百 RPS,那肯定哪有问题。


踩坑 1:ulimit 没改,压测先把自己压崩了

第一次用 wrk 压本地服务,跑了大概十几秒突然报了这么一串:

connect: Cannot assign requested address

把我整懵了——服务器没崩,压测端先崩了。

查了一下,是 Linux 默认的 socket 连接数限制。每建一个 TCP 连接就要占一个本地端口,范围默认只有 28000 左右。100 并发跑 30 秒,端口号很快就轮了一遍,老的 TIME_WAIT 还没释放,新的连不上。

解决方法:

# 调大可用端口范围
sudo sysctl -w net.ipv4.ip_local_port_range="1024 65535"

# 加快 TIME_WAIT 回收(非服务端,是压测端)
sudo sysctl -w net.ipv4.tcp_fin_timeout=15

这个是压在压测机上设的,跟目标服务器没关系。

踩坑 2:keep-alive 开没开,数据差十倍

有一次给一个 HTTP 接口做压测,一开始 wrk 默认走 keep-alive,RPS 大概 5000 多。后来因为想测短连接场景加了个 --header "Connection: close",结果 RPS 直接掉到 400。

同一个接口,差了一个数量级。

原因是:短连接每次请求都要新建 TCP 连接,TCP 三次握手 + TLS 握手的开销远大于真正的业务处理时间。真实场景中,大部分客户端(浏览器、API 网关)都会复用连接,所以 keep-alive 的结果更有参考价值。但如果你的客户端是高频率建短连接的场景,那也得按短连接来测。

关键不是哪个更快,是搞清楚你的真实流量长什么样

踩坑 3:嘿,别在本地压本地

有段时间我图省事,服务器上起服务,就在同一台机器上跑 wrk 压它。RPS 看着挺高,美滋滋上线,结果被真实流量教做人。

后来想明白了:本地压本地的结果不能信。loopback 接口走的是内核内部的虚拟网络,没有真实的网卡传输延迟和带宽瓶颈,P99 延迟会好看很多。

正确的做法是——从另一台机器压,或者至少设个独立的压测机。条件有限的话,在同一个局域网内的另一台机器也行,至少经过了一次物理网卡。

踩坑 4:wrk 的线程数和 CPU 的恩怨

wrk 默认开的线程数和机器 CPU 核数有关。我第一次在 2 核小机器上跑,直接 wrk -t12 -c400,想着并发越高越猛。结果跑出来 RPS 很低,CPU 全在抢线程上。

wrk 的线程主要是用来做并发管理的,不是越多越好。经验值:线程数不超过 CPU 核数。4 核机器用 -t4,8 核用 -t8。再多的话线程切换就会吃掉本来给业务的算力。

踩坑 5:只看 RPS 不看 P99

早期我只看 RPS,觉得数字高就是好。直到有一次压测 RPS 有 8000,自我感觉良好,结果生产上一有流量峰值就超时。

回去翻 wrk 输出,发现 p99 延迟已经到了 1.8 秒,但我一直没注意。

RPS 高不代表响应快。 一个系统可能在最优条件下吞吐很高,但一旦有波动,尾巴延迟就变得很难看。现在我养成了习惯:每次压测先看 p99,p99 在 500ms 以内算及格,200ms 以内算健康。


经过这些教训,我现在走的工作流比较固定了:

# 第一步:快速摸底(10 秒,100 并发)
wrk -t2 -c100 -d10s http://target/api/test

# 第二步:逐步加压(100→200→500 并发)
wrk -t4 -c200 -d30s http://target/api/test
wrk -t4 -c500 -d30s http://target/api/test

# 第三步:长稳测试(3-5 分钟看是否有内存泄漏)
wrk -t4 -c200 -d300s http://target/api/test

每一步都盯住这三个东西:RPS 有没有掉,p99 有没有涨,错误率是不是 0

如果第二步到第三步之间 RPS 突然掉了一截,大概率是连接池满了或者 GC 触发了。这时候就得回代码里看慢在哪。


Hey 的好处是输出更人性化,特别是想看详细延迟分布的时候:

hey -n 10000 -c 100 http://target/api/test

输出里有一段长这样:

Latency distribution:
  50% in 23.45ms
  75% in 35.12ms
  90% in 52.33ms
  95% in 78.90ms
  99% in 145.67ms

一眼看出大多数请求的响应时间,比 wrk 的统计更细。

我现在一般 wrk 做吞吐摸底,hey 做延迟分布分析。


说实话,压测这件事技术上并不难,装个工具敲几个参数就能跑出数据。真正的难点在于知道这些数据意味着什么,以及发现数据不对之后往哪个方向排查

上面这 5 个坑,基本都是我亲自翻过车之后才搞明白的。现在每次上线新接口之前走一遍压测流程,心里踏实很多——至少知道它在生产上遇到流量的时候会是什么表现,而不是等用户来告诉我。

踩过之后自然就会注意了。

评论一下?

OωO
取消