☰
物联网平台接口 P99 很高怎么办?Spring Boot 接口压测与压测客户端踩坑实录
2026/10/13 10:15:58 网站建设 项目流程

真实运行截图:


一、压测要回答的不是"快不快",是三个数

很多同学一提压测就说"看看能不能扛住 1 万并发",这个目标太虚。一次压测真正要带回来的是三个数:

  1. 吞吐量(req/s)——单位时间内处理完多少请求;
  2. 分位数延迟(P50 / P95 / P99)——99% 的请求在多少毫秒内返回;
  3. 错误率——压测中失败的请求占比。

为什么必须看分位数而不能只看平均?我项目里 9 月 22 日那次 JMeter 压测就是活例子(数据来自D:\dev\iot-device-platform\loadtest\README.md,2026-09-22 实测):

指标数值
总请求数136,892
吞吐量2,285 req/s
平均响应36.5 ms
P507 ms
P99422 ms
错误率0.00%

平均 36.5ms 看着很健康,但 P99 是 422ms——最慢的 1% 请求比一半以上的请求慢 60 倍。用户刷看板时"偶尔卡一下"的体感,就藏在这 1% 里,平均数根本照不出来。

二、这次我先把项目跑起来,重新压了一遍

为了让文章有当场可复现的数据,今天(10-09)我把项目用默认 H2 profile 重新起在 8080 端口(不依赖 EMQX,MQTT 连不上只是 warning,不影响 HTTP 接口),先注册 2 台压测设备确认接口活着(见上面第三张截图),然后对热点读接口GET /api/devices做并发压测。

第一个版本的压测脚本我直接写在浏览器页面里跑,结果翻车了——这正是本文最大的价值。

三、坑一:用浏览器当压测客户端,64 并发只压出 247 req/s

浏览器页面里用fetch发 64 个并发请求、持续 15 秒,实测结果:

  • 总请求数 3,766,吞吐量只有 247 req/s
  • 平均响应 256ms,P50 210ms,P99 828ms

同一台机器、同一个服务,稍后换个客户端能压出4191 req/s,相差17 倍。原因不是服务端抖了,而是浏览器的连接管理:Chromium 对同一个 HTTP/1.1 源最多只开6 条 TCP 连接,64 个 fetch 排队抢 6 条通道,测出来的"响应时间"大半是在排队,不是服务端在处理。

这就是压测的第一课:数字难看时,先怀疑压测客户端,再怀疑服务端。项目里那次 JMeter 报告我特意写了句"瓶颈在压测端并发模型而不是服务端"(loadtest\README.md原文),这次算是用另一条路径又验证了一遍。

顺带说一句:浏览器压测不是不能用,但它适合做单用户延迟采样,不适合做并发吞吐压测。

四、换 Node/undici 重压:4191 req/s、P99 32ms

Node 的内置 fetch(undici)没有每源 6 连接的限制,才是合格的压测客户端。核心代码(真实脚本,来源ai-content-studio\tools\bench-node-1009.cjs,已删减注释):

asyncfunctiononeShot(){constt0=performance.now();letstatus=0;try{constr=awaitfetch(URL,{cache:'no-store'});awaitr.arrayBuffer();status=r.status;}catch(e){status=-1;}return{ms:performance.now()-t0,status};}// 预热 500 次(丢弃 JIT 冷启动请求),再 64 个 worker 并发跑满固定时长for(leti=0;i<500;i++)awaitoneShot();constlat=[];leterrors=0;constt0=performance.now();conststop=t0+DURATION;asyncfunctionworker(){while(performance.now()<stop){constr=awaitoneShot();if(r.status!==200)errors++;lat.push(r.ms);}}awaitPromise.all(Array.from({length:CONC},worker));lat.sort((a,b)=>a-b);constpct=p=>lat[Math.min(lat.length-1,Math.floor(lat.length*p))];// 输出:total / rps / avg / p50 / p90 / p95 / p99 / max / errRate

实测(64 并发 × 15 秒):

指标数值
总请求数62,884
吞吐量4,191 req/s
平均响应15.3 ms
P5014.5 ms
P9019.5 ms
P9522.7 ms
P9932 ms
最大65.7 ms
错误率0.00%

注意两个细节:

  • 预热必须做。我第一轮没预热时只有 2266 req/s、P99 57.8ms,JIT 冷启动把前几秒的慢请求全算进分位数了;预热 500 次后才是稳定状态的真实水平。
  • 这次 P50 14.5ms 到 P99 32ms 只差 2 倍、最大 65.7ms,长尾很短——因为空库查询真的没什么可慢的。

五、4191 和 2285 哪个准?都不准,也都准

有人会问:9 月 JMeter 压出 2285 req/s,今天 Node 压出 4191 req/s,是不是服务端变快了一倍?

不是。这两组数字根本不可比,因为压测环境三要素全变了:

  1. 数据集不同:9 月那次打的是 MySQL profile、目标设备有 40+ 条遥测记录;今天 H2 是空内存库,连磁盘 IO 都没有;
  2. 客户端不同:JMeter 100 线程 vs undici 64 并发,客户端本身的开销和调度完全不同;
  3. 机器状态不同:9 月那次 JMeter 和服务端同机抢核(README 里写了这是 P99 422ms 的主要来源之一),今天压测端是另一个进程模型。

所以压测报告必须把环境写死在标题旁边:客户端模型、数据集、机器部署、JVM 参数,缺一个数字就没意义。这一点在面试里说出来,比背出任何一个具体数字都加分。

六、服务端为什么扛得住:三道"防打爆"设计

压测能扛,不是 Tomcat 厉害,是接口设计时就没留打爆自己的口子。三处真实代码:

① 参数硬上限(来源src\main\java\com\iothub\data\DataPointController.java):

privatestaticfinalintDEFAULT_LIMIT=100;privatestaticfinalintMAX_LIMIT=1000;intsize=limit==null?DEFAULT_LIMIT:Math.min(limit,MAX_LIMIT);Pageablepageable=PageRequest.of(0,size,Sort.by(Sort.Direction.DESC,"id"));

调用方传limit=999999也会被钳到 1000——遥测数据是无界的,不设上限等于把内存交给调用方管。

② 查询走索引(来源src\main\java\com\iothub\data\DataPoint.java):

@Entity@Table(indexes=@Index(name="idx_datapoint_device",columnList="deviceId"))publicclassDataPoint{...}

时序表是全平台数据量最大的表,deviceId 上没有索引,压测第一个死的就是它。

③ 关掉 open-in-view(来源src\main\resources\application.yml):

spring:jpa:open-in-view:false

不持有数据库连接到视图渲染结束,连接池才能在高并发下快速周转——这也是很多"压不上去"案例里藏得很深的一条。

七、P99 真的高了,按这个顺序查

回到标题的问题:压测发现 P99 很高怎么办?按我踩过的经验,排查顺序应该是:

  1. 先看压测客户端:连接数有没有上限、有没有和被测服务抢 CPU(今天 247 req/s 的教训);
  2. 看 GC 日志:长尾里最常见的常驻嘉宾,P50 和 P99 差几十倍多半有它的份;
  3. 看慢 SQL:索引命中了吗?有没有深分页(OFFSET 100000这种);
  4. 看连接池:HikariCP 默认 10 个连接,64 并发一来就在池上排队;
  5. 最后才是"加机器"。

顺序反了,就会出现"调了一周 JVM 参数,其实瓶颈在压测脚本"的惨剧。

写在最后

我是软件工程在读,正在从零搭一个物联网设备接入平台(Spring Boot + EMQX + MySQL),这个压测是平台性能这条支线的第一篇。下一篇计划写共享订阅(shared subscription)或者 Kafka 解耦。如果这篇对你有用,评论区聊一句你压测踩过的坑,比点赞更让我高兴。


本文所有数据均为本机实测:JMeter 数据来自项目loadtest\README.md(2026-09-22),Node 压测数据为 2026-10-09 当场实测,代码均来自项目真实源码,截图为真实运行画面。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询