背景
有一次线上接口突然变慢,平均响应时间从几百毫秒涨到一秒多。最开始大家都在看数据库、看网络、看下游接口,但查了一圈都没有特别明显的异常。
后来顺着调用链一点点拆,才发现问题并不是某一个点“特别慢”,而是线程池参数和重试策略叠加后,把整体延迟拉高了。
为什么这种问题容易误判
这类问题的麻烦之处在于:
- 单次调用看起来没那么离谱
- 慢 SQL 不明显
- 下游接口偶尔抖动,但也没到不可用
- 业务日志里只有“接口变慢”,没有直接报错
结果就是每一层看起来都“有点问题”,但又不像根因。
先看线程池:队列太长不代表系统稳定
当时我们的线程池配置偏保守,核心线程数不高,队列长度却比较大。平时流量平稳时没什么问题,但一遇到下游波动,请求就开始在队列里排队。
表面上看,线程池没有拒绝任务,好像很稳定;实际上用户请求已经在无声地等待了。
这类配置的典型风险是:
- 拒绝少,但延迟高
- 监控容易误判为“系统还能扛”
- 高峰期耗时不断堆积
再看重试:一次小抖动被放大成整条链路变慢
更关键的是,我们在下游调用上加了重试,而且重试间隔和超时时间都不算短。于是出现了这样的情况:
- 第一次调用抖动
- 业务线程进入等待
- 重试再次占用线程
- 队列继续积压
单看重试逻辑,它是为了提高成功率;单看线程池,它也不是完全不合理。但两者叠加之后,系统的平均响应时间就被整体拉上去了。
这类问题怎么查更有效
后来我比较认可的排查方式是分成三步:
- 先看线程池活跃线程数、队列长度和拒绝数
- 再看慢请求里有多少次发生了重试
- 最后把重试耗时和排队耗时加起来看总账
如果只盯着接口平均耗时,很难意识到“排队时间”其实已经占了大头。
这次排查给我的几个提醒
- 线程池的目标不是“尽量不拒绝”,而是“在系统可控范围内处理请求”
- 队列过长会掩盖问题,让延迟在系统内部慢慢堆积
- 重试不是免费的,提高成功率的同时也会放大资源占用
- 涉及外部依赖的链路,必须同时看超时、重试和并发配置
总结
这次接口变慢最容易让人误判的地方,就是每个点单独看都不算致命,真正的根因在于线程池参数和重试策略共同作用。排查线上慢请求时,我越来越觉得“组合效应”比“单点故障”更值得警惕。
如果系统已经接了很多外部服务,这类问题以后大概率还会再遇到,提前把排队时间和重试行为纳入监控会省很多事。