跳到主要内容

微服务调用链排查

问题

微服务架构中,一个接口调用链涉及多个服务,出现超时、雪崩效应怎么排查和处理?

答案

调用链问题

当 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: 什么是服务雪崩?怎么防止?

答案

一个下游服务故障,导致上游所有依赖它的服务线程被阻塞,最终整个链路不可用。

防止措施:

  1. 超时设置:每个调用设合理超时
  2. 熔断器:故障率高时自动断开
  3. 线程隔离:不同下游用独立线程池
  4. 降级:提供 fallback 逻辑

详见 熔断与降级

Q2: 熔断器的三种状态?

答案

  • Closed:正常状态,请求正常通过
  • Open:故障率超阈值,所有请求直接走降级
  • Half-Open:等待一段时间后放少量请求试探,成功则恢复

Q3: 微服务之间如何传递 TraceId?

答案

  • HTTP 调用:通过请求 Header 传递(如 X-Trace-Id
  • MQ:放在消息属性中
  • 跨线程:通过 MDC + 装饰器传递

详见 链路追踪

相关链接