接口超时排查
问题
线上接口 RT 突然从 100ms 飙升到 5s,甚至超时,如何排查?
答案
排查思路
第一步:确定慢的范围
日志分析
# 看最近 1 小时哪些接口超时最多
grep "timeout\|timed out" app.log | awk '{print $5}' | sort | uniq -c | sort -rn | head 20
第二步:链路追踪定位
通过 SkyWalking / Zipkin / Jaeger 查看请求链路,看 Span 耗时:
链路追踪结果
OrderController.createOrder 5200ms ← 总耗时
├─ OrderService.checkInventory 50ms ← 正常
├─ OrderMapper.insert 80ms ← 正常
├─ PaymentClient.createPayment 4800ms ← 这里慢!
│ └─ HTTP POST /api/payment 4800ms
└─ OrderMapper.updateStatus 20ms
定位到问题出在下游支付服务调用。
第三步:按方向深入排查
方向 1:数据库慢
排查慢 SQL
// 检查慢查询日志(见慢 SQL 排查)
// 检查连接池是否打满
HikariPoolMXBean poolProxy = ((HikariDataSource) dataSource).getHikariPoolMXBean();
log.info("Active: {}, Idle: {}, Waiting: {}",
poolProxy.getActiveConnections(),
poolProxy.getIdleConnections(),
poolProxy.getThreadsAwaitingConnection());
方向 2:Redis 慢
Redis 排查
# 查看慢查询日志
redis-cli slowlog get 10
# 查看大 Key
redis-cli --bigkeys
# 查看连接数
redis-cli info clients
方向 3:下游服务超时
Feign 超时设置
feign:
client:
config:
payment-service:
connect-timeout: 2000 # 连接超时 2s
read-timeout: 5000 # 读超时 5s
# 配合熔断
resilience4j:
circuitbreaker:
instances:
payment:
failure-rate-threshold: 50
slow-call-duration-threshold: 3s
方向 4:线程池/连接池打满
线程池耗尽的典型表现
所有请求排队等待 → RT 急剧上升
日志出现 RejectedExecutionException
Tomcat 线程全部 WAITING / TIMED_WAITING
Tomcat 线程池监控
# application.yml
server:
tomcat:
threads:
max: 200 # 最大线程数
min-spare: 20 # 最小空闲
max-connections: 8192
accept-count: 100 # 等待队列
优化策略
| 策略 | 说明 |
|---|---|
| 超时设置 | 每一层调用都设合理超时 |
| 熔断降级 | 下游超时触发熔断,快速失败 |
| 缓存 | 热点数据缓存,减少下游调用 |
| 异步化 | 非核心链路异步处理 |
| 连接池调优 | 合理配置数据库/HTTP/Redis 连接池 |
| 预热 | 服务启动后预热缓存和连接池 |
常见面试问题
Q1: 排查接口慢的完整思路?
答案:
- 看监控:CPU、内存、GC、连接池水位
- 看链路追踪:找到慢的 Span
- 分方向深入:DB 慢查询 / Redis 大 Key / 下游超时 / 线程池满
- 优化:加索引 / 加缓存 / 加超时 / 加熔断
Q2: 超时时间怎么设置合理?
答案:
- 参考历史 P99 延迟,设置为 P99 的 2-3 倍
- 链路超时层层递减:网关 > 调用方 > 被调方
- 例:网关 10s → A 服务调 B 5s → B 调 DB 2s
Q3: 所有接口同时变慢,最可能的原因?
答案:
- Full GC:STW 导致所有请求暂停
- CPU 100%:线程切换增多,处理变慢
- 连接池打满:所有请求排队等连接
- 网络故障:带宽打满或网络抖动