微服务调用链排查
问题
微服务架构中,一个接口调用链涉及多个服务,出现超时、雪崩效应怎么排查和处理?
答案
调用链问题
当 F(积分服务)变慢 → E 等待 → C 等待 → A 等待 → 所有请求阻塞 → 雪崩效应。
排查流程
超时传递问题
超时传递问题
Gateway 超时: 10s
→ 订单服务调支付服务超时: 5s
→ 支付服务调风控超时: 5s ← 问题:5s + 5s = 10s 已超网关超时!
超时时间必须层层递减
网关超时 > 上游超时 > 下游超时,且要留余量给网络传输和本地处理。
合理的超时配置
# 网关层
zuul.host.connect-timeout-millis: 10000
# 订单服务调支付
feign.client.config.payment-service.read-timeout: 3000
# 支付服务调风控
feign.client.config.risk-service.read-timeout: 1000
熔断降级
Sentinel / Resilience4j 熔断
// Resilience4j 配置
resilience4j:
circuitbreaker:
instances:
pointService:
failure-rate-threshold: 50 # 失败率 50% 触发熔断
slow-call-rate-threshold: 80 # 慢调用率 80% 触发
slow-call-duration-threshold: 2s
wait-duration-in-open-state: 30s # 熔断 30s 后半开
permitted-calls-in-half-open: 5 # 半开时放 5 个请求试探
降级处理
@FeignClient(name = "point-service", fallback = PointFallback.class)
public interface PointClient {
@PostMapping("/api/points/add")
Result addPoints(@RequestBody PointDTO dto);
}
@Component
public class PointFallback implements PointClient {
@Override
public Result addPoints(PointDTO dto) {
// 积分服务不可用时,记录到补偿表,后续异步补发
compensationService.savePointTask(dto);
return Result.success("积分稍后到账");
}
}
线程隔离
Bulkhead 线程隔离
resilience4j:
bulkhead:
instances:
paymentService:
maxConcurrentCalls: 20 # 最大并发 20
maxWaitDuration: 500ms # 超过等待 500ms 快速失败
隔离策略:
- 线程池隔离:每个下游服务独立线程池,A 服务慢不影响 B
- 信号量隔离:限制并发数量,不创建新线程
服务依赖治理
| 策略 | 说明 |
|---|---|
| 核心链路降级 | 非核心服务(积分、通知)可降级 |
| 异步化 | 非实时操作用 MQ 异步 |
| 缓存兜底 | 下游不可用时返回缓存数据 |
| 快速失败 | 超时/熔断后立即返回,不等 |
常见面试问题
Q1: 什么是服务雪崩?怎么防止?
答案:
一个下游服务故障,导致上游所有依赖它的服务线程被阻塞,最终整个链路不可用。
防止措施:
- 超时设置:每个调用设合理超时
- 熔断器:故障率高时自动断开
- 线程隔离:不同下游用独立线程池
- 降级:提供 fallback 逻辑
详见 熔断与降级。
Q2: 熔断器的三种状态?
答案:
- Closed:正常状态,请求正常通过
- Open:故障率超阈值,所有请求直接走降级
- Half-Open:等待一段时间后放少量请求试探,成功则恢复
Q3: 微服务之间如何传递 TraceId?
答案:
- HTTP 调用:通过请求 Header 传递(如
X-Trace-Id) - MQ:放在消息属性中
- 跨线程:通过 MDC + 装饰器传递
详见 链路追踪。