头一次被业务方追着问“接口能扛多少并发”的时候,我心里是没底的。那时候连压测是什么都只是听说过,更别说从零搭一套压测方案了。后来被项目逼着啃了一遍Apipost的自研压测引擎,从100并发一路试到1万并发,总算是把“压力测试”这四个字从文档变成了自己的经验。这篇内容不扯理论,就把我当时从搭环境、配参数、跑压测到看报告的全过程写下来,包括踩过的坑、怎么定位瓶颈、哪些指标最容易骗人,希望给同样需要做接口压力测试的同学一条能直接走的路。
1. 压测到底在测什么:三个核心指标先对齐
很多人一提到“一万并发”,第一反应是“每秒一万个请求”,这个理解从根上就偏了。接口压力测试不是简单地把请求数拉满,而是要搞清楚系统在持续高压下,吞吐量、响应速度、稳定性能保持到什么程度。并发用户数、每秒请求数、响应时间这三者之间有紧密关系,但绝不是同一个东西。
我当时第一次跑压测,最懵的就是这一块:并发设到1000,QPS却只有800多,我以为是工具出问题了,后来才明白,并发数和QPS是两个维度。并发数可以理解为“同一时刻有多少个请求正在路上”,而QPS是“每秒实际完成了多少个请求”。如果每个请求平均要等100毫秒,那1000个并发大约只能产生几千QPS;如果每个请求要等1秒,那1000个并发可能只有几百QPS。这就是为什么高并发不等于高吞吐。
1.1 并发、QPS、RT,不把这些分清楚,后面全是白忙
先把我自己用的概念定义捋一遍,这些是后面看报告的基础。
- 并发数:同一时刻正在进行的请求数量。它代表了系统同时面对的活跃连接数,不完全等同于在线用户数。用户可能开着页面在思考,那个不叫并发请求。
- QPS / TPS:每秒完成的请求数/事务数。这是吞吐量的核心指标。QPS上不去,说明系统处理能力到顶了,加再多并发也只是排队。
- RT / 响应时间:单个请求从发出到收到响应的时间,通常会看平均值、P95、P99。均值容易被极端值拉高,P95和P99才能反映大多数用户的真实体感。
- 错误率:请求失败的比例。失败包括连接失败、超时、HTTP状态码异常,以及你主动设置的断言失败。
用一个生活化的例子:把接口当成超市收银台。并发就是涌进超市的顾客数量,QPS是每秒能完成结账的人数,RT是顾客从排队到结账完成花的时间,错误率是等了太久直接放下东西走人的顾客比例。收银台数量不变,进店顾客再翻十倍,结账速度也不会变快,只会让排队时间变长,最后有人放弃购买。接口服务也一样,线程池、数据库连接池、下游依赖都是“收银台”,压力上去了,瓶颈自然暴露。
1.2 为什么选择Apipost自研引擎来做这次压测
早期用传统开源压测工具时,最头疼的是环境搭建和脚本维护。要装运行环境、下载插件、写脚本、处理分布式施压机的同步问题,一套流程走下来,半天过去了。Apipost的自研引擎把“接口调试”和“压力测试”做在了同一个产品里,接口请求已经调通,切到压测模块直接复用同一套请求配置,省掉了脚本迁移的成本。
而且它的分布式施压模式不是简单的“多台机器各跑各的”,而是由引擎统一调度,把压力按策略分发到多台施压机上,报告数据也是聚合的。这一点在高并发场景下很重要:单台机器能模拟的并发其实很有限,因为受限于端口数、线程数和CPU资源,真正的万级并发需要多台施压机协同。Apipost自研引擎把这一层调度逻辑封装好了,不需要自己搭监控和汇总链路。
还有一个加分项是断言和错误分类。压测过程中如果出现业务返回值异常、响应超时、连接失败,报告里会分门别类列出来,而不是只给一个总错误数。排查问题的时候,这个分类能直接告诉你先看哪一层:是网络断了,还是服务端超时,还是业务逻辑报错。
2. 压测前的准备:环境、接口、数据,一个都不能少
压测最怕的是测了个寂寞。环境不对、数据不对、接口选得不对,最后得出的结论根本不能代表真实情况。我趟过的坑之一,就是在开发环境直接跑压测,开发环境数据库配置低、缓存也没预热,测出来的结果连参考价值都没有。所以这一章讲的不是工具操作,而是跑压测之前必须先想清楚的几件事。
2.1 测试环境怎么布置才不算白测
压测环境至少要满足三个条件:独立、接近生产、可重复。
- 独立:尽量不要在生产环境压,如果你没有完善的隔离和降级机制,一次压测可能影响线上真实用户。我当时用的是独立测试环境,服务端、数据库、缓存、网关都单独部署。
- 接近生产:服务实例数、CPU/内存配置、数据库连接池上限、JVM参数、缓存策略,这些关键配置要尽量和生产一致。如果测试环境只开了一个实例,生产却有十个,那测出来的单实例数据只能做参考,不能直接当容量评估结论。
- 可重复:环境要能重置。压测会在数据库里产生大量脏数据,也会打满连接池或文件句柄,最好有脚本能在每轮压测前把环境恢复到初始状态。
压测机和被压服务最好在同一内网,中间不要隔着一大段公网链路。公网抖动、带宽限制、运营商路由变化都会干扰结果,把网络问题误判成应用问题。我第一次做跨公网压测,QPS曲线像锯齿一样上下跳,最后排查了半天发现是压测机和服务器不在一个机房,走公网时报文重传严重。换到同一内网后,数据立刻稳了。
2.2 接口选型与测试数据构造
压测接口不能随便挑。我选的是一个典型的订单查询接口,既有数据库查询,也有少量逻辑处理,还有下游状态服务调用,覆盖了服务端常见的几种消耗场景。如果你选一个只返回固定字符串的“Hello World”接口,测出来的QPS再高,也没有实际业务参考意义。
接口定下来之后,最容易被忽略的是测试数据。直接用同一个用户ID并发打一万次,会让服务端大量命中缓存,响应时间特别好看,但真实用户的数据不可能完全一样。这里需要做参数化。我提前生成了一批CSV数据文件,字段类似这样:
user_id,expect_amount 10001,128 10002,256 10003,64 10004,512然后在Apipost压测场景里把请求参数绑定到CSV字段上,一个用户ID只会在一次请求里使用,用完之后下一条数据继续顶上来。这样能模拟出比“单用户刷缓存”真实得多的场景。如果你要测登录接口,还要准备一批有效账号;如果要测列表接口,要注意每页数据量接近真实用户习惯,别把参数调成极限值来“自我感动”。
还有一个容易被忽略的点:测试数据量的规模要和并发量匹配。要压到一万并发,CSV数据至少准备两三万条,最好十万条,不然数据轮转回来又变成同一批用户了。
2.3 压测前的基础检查清单
在真正点“开始压测”之前,我习惯把这几项过一遍:
- 服务端日志级别不要是DEBUG,否则压测过程中日志疯狂输出,CPU先被日志打满;
- 数据库慢查询日志打开,方便事后倒查慢SQL;
- 基础监控确认可用,至少能看到CPU、内存、磁盘IO;
- 压测机上文件描述符上限调大,默认1024根本扛不住;
- 被压服务的方法链路里如果有关联外部系统,确认外部系统能承受压力,或者做好mock。
这些看起来琐碎,但每一条都是我在实际压测中被坑过之后才补上的。日志级别那个问题最隐身:第一次压测还没到1000并发,CPU先100%了,查了半天发现是应用在打印每一行请求明细,关掉之后CPU立刻降下来了。
3. 实操:从创建压测场景到一万并发跑起来
准备做完,下面进入正式的Apipost压测实操。我带客户做过好几轮性能验收,也经常在内部给团队演示这套流程,步骤已经比较固定了。Apipost的界面入口在不同版本会有一点差异,但整体逻辑是通的,找到“压力测试”模块,按下面的思路做就行。
3.1 第一步:在Apipost里创建压测场景
你先在Apipost里把目标接口调试通过,确认请求头、请求体、鉴权参数都正常。然后进入压测模块,新建一个压测场景,选择这个接口作为压测目标。场景创建时会自动带入接口的URL、Method、请求头和请求体,不需要重新填一遍。
我这里踩过一个小坑:如果接口带了登录态Token,而Token有过期时间,压测跑到中途Token失效,所有请求突然变成401,如果不看报告里的错误分类,你可能完全想不到是鉴权问题。后来我把Token过期时间调长,或者在场景里配置了动态获取Token的脚本,问题才解决。
创建场景后,先把请求参数改成变量绑定。比如把user_id占位成{{user_id}},然后指定数据源为之前准备好的CSV文件。Apipost会按字段名自动匹配,不需要写成复杂的映射关系。这一步做好,参数化就算接通了。
3.2 第二步:配置并发策略与阶梯加压
这里要重点讲“阶梯加压”。新手最容易犯的错就是直接把并发数拉到目标值,然后启动压测。我之前也这么干过,结果几轮数据全部作废,因为瞬间冲击会触发服务的限流、熔断甚至直接挂掉,你根本看不出系统真实的拐点在哪里。
正确的做法是由低到高、逐级加压,每一步观察一段时间,让系统从“稳定”过渡到“饱和”,再进入“过载”。我这次一万并发目标,实际是按五轮走的:
| 阶段 | 并发数 | 持续时间 | 目的 |
|---|---|---|---|
| 第一轮 | 100 | 2分钟 | 功能验证,基础链路是否正常 |
| 第二轮 | 500 | 2分钟 | 观察初步性能趋势 |
| 第三轮 | 1000 | 3分钟 | 接近日常峰值,排查隐患 |
| 第四轮 | 3000 | 3分钟 | 突破常规负载,寻找拐点 |
| 第五轮 | 5000以上逐步到10000 | 5分钟 | 极限验证,确认最大承载量 |
如果你用的Apipost版本支持在一个场景里配置多段阶梯,就直接按阶段设置并发数和持续时间;如果只能配一个固定并发,就多建几个场景,按顺序依次启动,效果也是一样的,只是要手动切换。
每轮之间建议留出1到2分钟的间隔,让服务端连接池、线程池有释放时间,不然上一轮的堆积会污染下一轮的数据。
3.3 第三步:启动压测与实时监控
配置完成后,启动压测。Apipost压测过程中会实时刷新QPS、平均响应时间、错误率这些指标曲线,这个时候不要干等,我一般重点盯三件事:
- QPS曲线:是否随并发数增加而同步上升?如果并发上去了QPS不再变,说明系统已经打到吞吐上限,往后加并发只会增加排队时间。
- 错误率拐点:错误率开始抬升的时机,就是系统告别“舒适区”的位置。这个点决定了容量预警水位。
- RT曲线:响应时间是否快速上扬?上扬角度越陡,说明资源竞争越严重,系统余量越小。
如果用了分布式压测,我还会切到施压机视角看每台机器的CPU、内存和流量。单台施压机如果CPU已经打满,说明压力源本身成了瓶颈,结果会失真,这时候需要增加施压机节点。
压测过程中如果看到错误率突然飙升,我的习惯是立刻截图记录时间点,然后从服务端日志里找对应时间段发生了什么。很多时候瓶颈不会在压测过程中告诉你答案,而是要在事后分析时把时间点对齐,才看得出因果。
4. 从100并发到10000并发:一次完整的跑测记录
这一章就直接把我那次的压测数据摆出来,结合Apipost报告的维度做分析。数据是模拟示例,但趋势非常典型,和你自己跑出来的结果应该会很像。
4.1 五轮压测的数据记录
| 压测轮次 | 并发数 | 实测QPS | 平均RT | P95 RT | 错误率 |
|---|---|---|---|---|---|
| 第一轮 | 100 | 约980 | 110ms | 180ms | 0% |
| 第二轮 | 500 | 约2600 | 190ms | 360ms | 0% |
| 第三轮 | 1000 | 约3350 | 290ms | 620ms | 0.03% |
| 第四轮 | 3000 | 约3550 | 780ms | 1900ms | 0.9% |
| 第五轮 | 5000 | 约3700 | 1250ms | 2800ms | 2.7% |
| 第六轮 | 10000 | 约3800 | 2300ms | 5200ms | 7.4% |
从100并发到1000并发,QPS增长非常可观,从980涨到3350,说明系统在正常负载下还有不少余量,RT也保持在300毫秒以内。到了3000并发,QPS增速明显放缓,RT快速上升,错误率开始出现,这基本就是系统的拐点区域。从5000并发到10000并发,QPS几乎原地踏步,还是3700到3800的区间,但RT翻倍、错误率翻倍再翻倍,说明系统已经进入过载区,新增的并发全部转化成了排队和超时。
4.2 这份数据说明了什么
读懂这份报告的核心就一句话:一个系统的最大吞吐能力不是无限的,超过拐点后并发再高,QPS也不会继续涨,反而会把响应时间和错误率拖垮。
我那次测试里,服务端最大有效吞吐大约在3700到3800 QPS之间。这意味着即使把并发提到一万,实际每秒能成功处理的请求也只有3800左右,剩下的所有请求都在等。对这个结果,业务侧需要回答的问题是:你的真实用户能接受多长的响应时间?如果能接受2秒左右,那一万并发下错误率7%还是有点危险;如果能接受3秒,那这个数字勉强可以,但需要进一步优化稳定性。
报告里还有两个细节值得注意:
- P95远高于平均值:比如10000并发时平均RT 2.3秒,但P95已经到5.2秒,说明有一小部分请求非常慢,这可能不是单纯的排队问题,而是某个下游依赖间歇性超时。
- 错误类型分布:Apipost报告里能看到错误分类,我当时大量错误是“读超时”,说明网络连接没问题,而是服务端迟迟不回包,问题的范围被缩小到应用层和数据库层。
4.3 瓶颈定位与优化过程
根据报告里的线索,我重点查了服务端的线程池状态和数据库慢查询。打开慢查询日志,发现这个订单查询接口有一个SQL在数据量大的情况下走了全表扫描,执行时间从几十毫秒涨到一千多毫秒。加了联合索引之后,重新跑3000和5000并发,QPS提升到4500以上,平均RT降到600毫秒以内,错误率降到0.2%以下。
还有一个意外发现:日志里有一个外部状态服务调用串在请求链路上,偶尔出现2秒超时。我把这个调用改成异步并加超时熔断,P95立刻降了一大截。这个问题的隐蔽之处在于,它平时只影响少数请求,但并发高了之后,一旦下游服务开始排队,它的超时时间会成为整个链路的“拖油瓶”。这类问题如果没有压测,很难在日常功能测试里发现。
5. 压测中一定会遇到的坑与排查实录
工具用熟了以后,压测本身不难,难的是压测过程中暴露出来的问题,以及压测系统本身的问题。这一章是我自己踩过或者帮别人排查过的典型坑,每条都有真实场景,可以直接对照自己的情况排查。
5.1 压测机端口不够,大量“Cannot assign requested address”
单台压测机往上压并发时,最常遇到的是客户端端口耗尽。Linux默认的临时端口范围有限,而且TCP连接断掉后进入TIME_WAIT状态,端口不会被立即复用。并发一高,新连接就申请不到本地端口了,报告里错误类型会显示大量连接失败。
我当时的处理方法是调整压测机内核参数:
# 扩大临时端口范围 sysctl -w net.ipv4.ip_local_port_range="1024 65535" # 加快TIME_WAIT连接回收 sysctl -w net.ipv4.tcp_tw_reuse=1 sysctl -w net.ipv4.tcp_fin_timeout=15同时把压测机的文件描述符上限调高,然后重启压测程序再跑。如果你用的是Apipost分布式压测,这类配置由施压机所在的平台统一管理,基本不用自己操心,但如果是本机压测,这几条命令能解决大部分连接数瓶颈。
5.2 服务端连接突然被重置或拒绝
有一次压测到2000并发左右,报告里突然出现大量连接被重置的报错,而且和客户端端口耗尽无关,因为换了一台客户端依然复现。最后排查到服务端,发现是接入层网关的连接数配置太低,连接池满了以后直接拒绝新连接。
这属于典型的“前置资源先耗尽”问题。网关的连接数上限、Tomcat/Jetty的线程池大小、数据库连接池大小,这三层是最常见的瓶颈点。排查顺序我一般是从外到内:先看接入层连接数,再看应用线程池,最后查数据库连接池和慢SQL。
5.3 压测结果“好得离谱”,先怀疑一下参数化
如果压测结果QPS高到不真实,比如一个带数据库查询的接口,单机压测跑出几万QPS,而且RT还很稳,大概率是测试数据没有做参数化,所有请求都在打缓存或者都在查同一条数据。数据全部命中缓存之后,测到的是缓存性能,不是接口性能。
我当时帮同事排查过一次“一万并发无压力”的报告,一查发现他所有请求的user_id都是同一个,数据库这层根本没被真正打透。解决办法就是前面说的CSV参数化,把请求参数做成一批真实分布的用户数据,数据量足够大,别让服务端把数据“记住”。
5.4 常见问题速查表
| 症状 | 可能原因 | 排查思路 |
|---|---|---|
| 大量连接失败 | 端口耗尽 / 连接数上限 | 调压测机端口范围,查服务端连接池 |
| 错误集中在“读超时” | 应用处理慢,下游依赖超时 | 查慢SQL、外部调用耗时、GC日志 |
| QPS不随并发增长 | 服务端吞吐已达上限 | 查CPU、线程池、数据库瓶颈 |
| RT突然尖刺 | 锁竞争 / GC停顿 / 缓存穿透 | 对比监控曲线,定位时间点 |
| 压测结果异常高 | 参数化不足命中缓存 | 检查测试数据分布,扩大数据池 |
| 压测机CPU 100% | 施压机能力不够 | 增加施压节点,走分布式压测 |
注意:调整Linux内核参数和连接池配置时,先在测试环境小范围验证,不要直接在生产环境操作,尤其是网络相关参数,改错了影响面很大。
最后说个我自己的习惯。压测结果出来后,我不会只盯着最大QPS看,而是先看错误率拐点出现在第几轮,再看P99涨到多少。性能问题不是想出来的,是一次一次压出来、一层一层剥出来的。工具只是一部分,关键是你能不能从数据里读懂系统正在经历什么。希望你也把这一套流程跑通,以后别人问你“接口能扛多少并发”,你也能随手甩出一份报告来。