接口频繁超时、报错?后端常见排查思路总结
接口突然变慢、大面积超时、间歇性 5xx,是后端工程师最常见的"高压现场"。这类问题往往不是单一原因造成的,而是链路多环节叠加的结果。本文按"从外到内、从粗到细"的顺序,整理一套可落地的排查思路,适合新人照着用,也适合老手查漏补缺。
一、先稳住现场:快速止血与信息收集
在深入分析前,先保证两件事:别让事故扩大,同时把证据留住。
1. 快速止血三板斧
-
限流降级:对非核心接口或特定用户开启限流,避免雪崩。
-
回滚变更:如果最近刚发版 / 改配置 / 调参数,第一时间考虑回滚。
-
切流:灰度环境出问题,切回稳定版本;单机房异常,切流量到其他机房。
2. 信息收集清单
-
现象:哪些接口超时?报错码是什么(504 / 502 / 500 / 429)?
-
范围:是所有接口都慢,还是某几个接口?是某个机房/可用区,还是全局?
-
时间:什么时候开始?是否和发版、流量高峰、定时任务时间吻合?
-
关联系统:DB、缓存、MQ、第三方接口是否有告警?
小技巧:先在监控大盘上扫一眼 CPU、内存、QPS、RT、错误率,往往能直接排除一半可能性。
二、网络与接入层:先排除"非代码问题"
很多问题看起来像后端 Bug,其实在网络层。
1. 客户端 → 负载均衡(SLB / Nginx)
-
超时配置不一致:客户端超时 < 网关超时 < 服务超时,容易导致客户端提前断开,服务端还在处理。
-
连接数打满 :
TIME_WAIT过多、somaxconn过小,导致新连接被拒绝。 -
带宽瓶颈:公网/专线带宽打满,RT 直线上升。
-
TLS 握手慢:证书校验失败、OCSP 响应慢、加密套件性能差。
排查工具 :curl -w、telnet、ss -antp、tcpdump。
2. 网关层(Nginx / API Gateway)
-
worker_connections是否耗尽? -
keepalive配置是否合理? -
是否存在大量 499(客户端提前关闭)、502(后端不可用)、504(后端超时)。
-
是否开启了不合理的日志格式(例如记录
$request_body),导致磁盘 IO 飙升。
三、应用服务层:重点排查"慢"和"堵"
这是最常见的问题发生地。
1. 线程池 / 协程池
-
线程池打满:HTTP 线程、业务线程、异步线程全部 busy,新请求排队或直接拒绝。
-
阻塞操作占满线程 :在 Web 线程里调用
sleep()、同步等待、大对象锁等。 -
协程调度异常:Go / Java 协程泄漏、频繁切换导致 CPU 飙高。
排查工具:
-
Java:
jstack、Arthas(thread、trace) -
Go:
pprof(goroutine、block)
2. 死锁与锁竞争
-
数据库行锁、分布式锁未释放。
-
synchronized / Lock 范围过大,热点数据串行化严重。
-
并发集合使用不当,导致隐性阻塞。
表现 :部分接口 RT 缓慢但 CPU 不高,线程堆栈大量 BLOCKED / Waiting。
3. 内存与 GC
-
频繁 Full GC / STW:老年代被打满,接口"假死",RT 飙升。
-
内存泄漏:缓存无限增长、List 未及时清理、ThreadLocal 未 remove。
-
大对象分配:一次性加载几十万条数据到内存。
排查工具:
-
JVM:
jstat -gc、jmap -histo、GC 日志 -
Go:
pprof heap、runtime.ReadMemStats
4. 日志与磁盘 IO
-
同步写日志 + 高频打印 DEBUG 日志。
-
日志文件未切割,单文件几十 GB。
-
磁盘 inode 用尽或 IO util 100%。
典型现象:CPU iowait 高,应用 RT 抖动明显。
四、数据层:数据库与缓存是重灾区
绝大多数超时,最终都能追到 DB 或缓存。
1. 数据库常见问题
-
慢 SQL :缺少索引、索引失效、全表扫描、大分页(
limit 100000, 20)。 -
连接池耗尽 :
maxPoolSize太小,或连接未及时归还(忘记close())。 -
长事务:一个事务里包含 RPC 调用、MQ 发送,导致连接长期占用。
-
锁等待:更新同一行数据、外键无索引、间隙锁冲突。
排查手段:
-
查看慢查询日志。
-
show processlist/pg_stat_activity。 -
数据库监控:QPS、TPS、活跃连接数、锁等待时间。
2. 缓存问题
-
缓存穿透:大量请求不存在的 key,直接打到 DB。
-
缓存击穿:热点 key 过期瞬间,高并发请求涌向 DB。
-
缓存雪崩:大批 key 同时过期,或 Redis 宕机。
-
Big Key / Hot Key:单个 key 过大或访问过于集中,Redis CPU 飙高。
-
连接池问题:连接泄露、超时设置不合理。
排查工具 :Redis slowlog、monitor、info stats、redis-cli --bigkeys。
五、外部依赖:第三方接口与中间件
"我的服务没问题,但被别人拖垮了。"
1. 第三方接口
-
对方接口 RT 变慢甚至超时。
-
没有设置合理的超时时间和重试策略(尤其是重试风暴)。
-
没有熔断机制,下游抖动直接传导到上游。
应对:
-
超时 < 总接口 SLA。
-
重试次数 ≤ 1,并配合退避策略。
-
引入熔断(如 Sentinel、Hystrix)。
2. 消息队列
-
消费能力不足,消息堆积。
-
消费逻辑太重(例如单条消息处理几百毫秒)。
-
消费者频繁 Rebalance,导致消费暂停。
排查:MQ 控制台查看堆积量、消费速率、Rebalance 日志。
六、资源瓶颈:CPU、内存、带宽的系统性问题
有时不是哪个组件坏了,而是资源整体吃紧。
1. CPU
-
us 高:业务逻辑重、序列化/反序列化频繁、正则匹配复杂。
-
sy 高:线程切换频繁、系统调用过多。
-
iowait 高:磁盘 IO 瓶颈。
2. 内存
-
OOM Killer 杀进程。
-
Swap 被启用,性能急剧下降。
-
Page Cache 回收频繁。
3. 带宽
-
跨机房调用、大报文传输(例如一次性返回上万条记录)。
-
图片/文件未走 CDN,直接走接口。
排查工具 :top、htop、vmstat、iostat、iftop、nload。
七、代码逻辑:那些"隐蔽但致命"的问题
1. 循环里的远程调用
for (Order order : orders) {
UserInfo user = userService.getUser(order.getUserId()); // 每次都 RPC
}
✅ 改为批量查询或本地缓存。
2. 无界集合
static List<Event> events = new ArrayList<>(); // 只加不删
✅ 限制大小,或使用带过期时间的缓存。
3. 不合理的重试
-
对写接口做重试,导致重复下单、重复扣款。
-
重试风暴放大下游压力。
4. 异常处理不当
-
catch 后吞掉异常,只返回一个模糊的"系统错误"。
-
异常日志没有打印关键参数,难以复现。
八、推荐的标准排查流程(可直接当 checklist 用)
-
看监控:RT、QPS、错误率、CPU、内存、IO、网络。
-
查变更:最近 1 小时内是否有发版、配置变更、流量突增。
-
看日志:应用错误日志、网关日志、慢 SQL 日志、MQ 日志。
-
定位瓶颈:DB / Redis / 第三方 / 自身代码。
-
验证假设:用 Arthas / pprof / explain 等工具验证猜测。
-
修复 & 复盘:短期止血 + 长期优化 + 故障复盘。
九、预防胜于治疗:稳定性建设建议
-
监控告警:覆盖 RT P99、错误率、线程池使用率、连接池使用率。
-
压测:定期做全链路压测,明确各组件水位。
-
容量规划:根据业务增长预估 DB、缓存、带宽容量。
-
防御性编程:超时、重试、熔断、降级、限流缺一不可。
-
Code Review:重点审查循环调用、资源释放、锁的使用。
十、一句话总结
接口超时和报错,本质是在某个环节发生了**"等待"** 或**"拒绝"**。
排查的核心就是:沿着请求链路,找到那个最慢、最堵、最脆弱的点,然后解决它。