强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

微服务拆分精讲 / 第 17 章:故障排查

第 17 章:故障排查

分布式系统的故障排查是另一维度的挑战。一个请求经过 10 个服务,问题可能出在任何一个环节。


17.1 微服务常见故障类型

17.1.1 故障分类

┌──────────────────────────────────────────────────────────────┐
│                   微服务故障分类                               │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌────────────────┐  ┌────────────────┐  ┌────────────────┐ │
│  │ 网络故障        │  │ 服务故障        │  │ 数据故障        │ │
│  ├────────────────┤  ├────────────────┤  ├────────────────┤ │
│  │ • 网络分区     │  │ • 服务崩溃     │  │ • 数据不一致   │ │
│  │ • DNS 解析失败 │  │ • OOM          │  │ • 数据丢失     │ │
│  │ • 连接超时     │  │ • 死锁         │  │ • 数据损坏     │ │
│  │ • 带宽不足     │  │ • 线程池耗尽   │  │ • 缓存穿透     │ │
│  │ • 负载不均     │  │ • 内存泄漏     │  │ • 缓存雪崩     │ │
│  └────────────────┘  └────────────────┘  └────────────────┘ │
│                                                              │
│  ┌────────────────┐  ┌────────────────┐  ┌────────────────┐ │
│  │ 配置故障        │  │ 安全故障        │  │ 依赖故障        │ │
│  ├────────────────┤  ├────────────────┤  ├────────────────┤ │
│  │ • 配置错误     │  │ • 认证失败     │  │ • 上游服务不可用│ │
│  │ • 环境变量缺失 │  │ • 证书过期     │  │ • 下游服务超时  │ │
│  │ • 版本不兼容   │  │ • Token 失效   │  │ • 第三方 API 故障│ │
│  └────────────────┘  └────────────────┘  └────────────────┘ │
└──────────────────────────────────────────────────────────────┘

17.1.2 故障频率排行

排名故障类型频率影响范围
1网络超时极高局部-全局
2服务不可用局部
3内存泄漏/OOM单服务
4数据库连接池耗尽单服务-关联服务
5配置错误单服务
6线程池/连接池死锁单服务
7数据不一致业务层

17.2 分布式调试方法论

17.2.1 调试流程(RED 方法)

  RED 调试法:

  R - Reproduce (复现)
  ──────────────────
  • 确认问题现象
  • 收集错误日志和时间范围
  • 确定影响范围

  E - Explore (探索)
  ──────────────────
  • 查看链路追踪(TraceID)
  • 查看每个服务的日志
  • 查看监控指标(QPS、延迟、错误率)

  D - Diagnose (诊断)
  ──────────────────
  • 定位问题根因
  • 验证假设
  • 制定修复方案

17.2.2 使用 TraceID 串联调试

  场景:用户反馈下单失败

  Step 1: 从用户请求中获取 TraceID
  ──────────────────────────────────
  请求 Header: X-Request-ID: req-abc-123-def

  Step 2: 在链路追踪系统中搜索
  ──────────────────────────────────
  Jaeger/Tempo 搜索: traceID = req-abc-123-def

  Trace 结果:
  │
  ├── API Gateway (5ms) ✅
  │   └── Order Service (150ms) ❌ 超时
  │       ├── User Service (10ms) ✅
  │       ├── Product Service (15ms) ✅
  │       ├── Inventory Service (50ms) ✅
  │       └── Payment Service (85ms) ❌ timeout!
  │           └── Bank API: 连接超时

  Step 3: 定位根因
  ─────────────────
  Payment Service → Bank API 的调用超时
  原因:银行 API 网络抖动

  Step 4: 查看相关日志
  ─────────────────────
  Loki 查询: {service="payment-service"} | json | traceId="req-abc-123-def"

  日志结果:
  [ERROR] Failed to call Bank API: Connection timeout after 5000ms
  [WARN] Circuit breaker OPEN for Bank API

17.2.3 常用调试命令

# 1. 查看 Pod 状态
kubectl get pods -n production -l app=order-service
kubectl describe pod order-service-xxx -n production

# 2. 查看 Pod 日志
kubectl logs order-service-xxx -n production --tail=100
kubectl logs order-service-xxx -n production --previous  # 查看上一个容器的日志

# 3. 进入容器调试
kubectl exec -it order-service-xxx -n production -- /bin/sh

# 4. 查看资源使用
kubectl top pods -n production -l app=order-service

# 5. 查看服务端点
kubectl get endpoints order-service -n production

# 6. DNS 解析测试
kubectl run debug --image=busybox -it --rm -- nslookup order-service.production.svc.cluster.local

# 7. 网络连通测试
kubectl run debug --image=curlimages/curl -it --rm -- curl -v http://order-service:8080/actuator/health

17.3 性能排查

17.3.1 性能问题定位流程

  性能问题定位:

  ┌──────────────────────────────────────────────────────┐
  │  1. 确认问题现象                                      │
  │  ├── P99 延迟从 100ms 升到 2s                         │
  │  ├── 仅影响订单服务                                   │
  │  └── 上午 10 点开始                                   │
  └───────────────────────┬──────────────────────────────┘
                          ▼
  ┌──────────────────────────────────────────────────────┐
  │  2. 查看基础设施指标                                  │
  │  ├── CPU: 订单服务 Pod CPU 90%+ ❌                   │
  │  ├── Memory: 正常 60%                                │
  │  ├── Network: 正常                                   │
  │  └── Disk: 正常                                      │
  └───────────────────────┬──────────────────────────────┘
                          ▼
  ┌──────────────────────────────────────────────────────┐
  │  3. 查看应用指标                                      │
  │  ├── JVM Heap: 正常                                  │
  │  ├── GC: Full GC 频率增加                             │
  │  ├── Thread Count: 线程数 500+ (正常 200) ❌          │
  │  ├── DB Connection: 连接池 50/50 (满) ❌             │
  │  └── HTTP Connections: 大量 CLOSE_WAIT ❌            │
  └───────────────────────┬──────────────────────────────┘
                          ▼
  ┌──────────────────────────────────────────────────────┐
  │  4. 查看链路追踪                                      │
  │  ├── 大量请求卡在 DB 查询 (平均 800ms)               │
  │  ├── DB 查询:SELECT * FROM orders WHERE ... (慢查询)│
  │  └── 缺少索引                                        │
  └───────────────────────┬──────────────────────────────┘
                          ▼
  ┌──────────────────────────────────────────────────────┐
  │  5. 根因分析                                          │
  │  慢 SQL → 连接池等待 → 线程堆积 → CPU 升高 → 延迟飙升│
  └───────────────────────┬──────────────────────────────┘
                          ▼
  ┌──────────────────────────────────────────────────────┐
  │  6. 修复                                              │
  │  ├── 添加数据库索引                                   │
  │  ├── 优化 SQL 查询                                    │
  │  └── 增加连接池大小                                   │
  └──────────────────────────────────────────────────────┘

17.3.2 性能排查工具箱

工具用途使用场景
Prometheus + Grafana指标监控宏观性能趋势
Jaeger/Tempo链路追踪请求链路瓶颈
ArthasJava 在线诊断线程/方法级分析
async-profilerJava 性能剖析CPU/内存热点
jstat/jmapJVM 诊断GC/堆分析
tcpdump网络抓包网络层问题
strace系统调用追踪I/O 问题

17.3.3 Arthas 使用示例

# 安装 Arthas
curl -O https://arthas.aliyun.com/arthas-boot.jar

# 连接到目标 Java 进程
java -jar arthas-boot.jar

# 查看最繁忙的线程
thread -n 3

# 查看方法调用耗时
trace com.example.order.service.OrderService createOrder

# 查看方法调用栈
stack com.example.order.service.OrderService createOrder

# 查看对象属性
watch com.example.order.service.OrderService createOrder '{params, returnObj}' -x 2

# 反编译类
jad com.example.order.service.OrderService

# 监控方法调用(统计 QPS 和成功率)
monitor -c 5 com.example.order.service.OrderService createOrder

17.4 熔断与降级

17.4.1 熔断器模式回顾

  熔断器状态转换:

  正常流量 → CLOSED (所有请求通过)
                │
                │ 连续失败超过阈值 (如 5 次)
                ▼
           OPEN (所有请求直接拒绝,返回降级响应)
                │
                │ 等待超时 (如 30 秒)
                ▼
         HALF-OPEN (允许少量探测请求)
                │
         ┌──────┴──────┐
         │             │
     探测成功      探测失败
         │             │
         ▼             ▼
      CLOSED         OPEN

17.4.2 降级策略

策略说明示例
返回缓存数据使用缓存中的旧数据商品详情返回缓存
返回默认值返回预设的默认值推荐商品返回热门列表
功能降级关闭非核心功能关闭商品推荐
排队等待请求排队,稍后处理秒杀排队
友好提示返回用户友好的错误信息“系统繁忙,请稍后”

17.4.3 Resilience4j 降级配置

@Service
public class OrderService {

    @CircuitBreaker(name = "paymentService", fallbackMethod = "createOrderFallback")
    @Retry(name = "paymentService")
    @TimeLimiter(name = "paymentService")
    public CompletableFuture<Order> createOrder(CreateOrderCommand command) {
        // 正常逻辑
        PaymentResult result = paymentClient.createPayment(command);
        return CompletableFuture.completedFuture(new Order(command, result));
    }

    // 降级方法
    public CompletableFuture<Order> createOrderFallback(CreateOrderCommand command,
                                                         Exception e) {
        log.warn("支付服务不可用,订单进入待支付状态", e);
        // 降级策略:创建订单但标记为待支付
        Order order = new Order(command, OrderStatus.PENDING_PAYMENT);
        orderRepository.save(order);
        // 发送延迟消息,稍后重试支付
        messageQueue.send("payment-retry", new PaymentRetryMessage(order.getId()));
        return CompletableFuture.completedFuture(order);
    }
}

17.4.4 Resilience4j 配置

# application.yml
resilience4j:
  circuitbreaker:
    instances:
      paymentService:
        slidingWindowSize: 10
        failureRateThreshold: 50
        waitDurationInOpenState: 30s
        permittedNumberOfCallsInHalfOpenState: 3
        registerHealthIndicator: true
  retry:
    instances:
      paymentService:
        maxAttempts: 3
        waitDuration: 500ms
        retryExceptions:
          - java.io.IOException
          - java.net.SocketTimeoutException
        ignoreExceptions:
          - com.example.BusinessException
  timelimiter:
    instances:
      paymentService:
        timeoutDuration: 5s
        cancelRunningFuture: true

17.5 常见问题速查表

17.5.1 服务调用类

问题可能原因排查方法解决方案
服务调用超时下游服务慢/网络问题TraceID 追踪优化下游/增加超时
连接拒绝连接池满/服务未启动netstat / 日志扩大连接池
DNS 解析失败CoreDNS 异常/Service 不存在nslookup检查 Service 配置
502 Bad GatewayPod 未就绪/健康检查失败K8s 事件/日志修复健康检查
连接池耗尽连接泄漏/并发太高监控指标修复泄漏/扩容

17.5.2 数据类

问题可能原因排查方法解决方案
数据不一致分布式事务失败数据校验脚本补偿/修复数据
缓存穿透大量不存在的 key 查询监控 + 日志布隆过滤器/缓存空值
缓存雪崩大量 key 同时过期监控随机过期时间
慢 SQL缺少索引/全表扫描慢查询日志添加索引/优化 SQL
死锁并发更新同一行DB 死锁日志优化事务/减少锁范围

17.5.3 资源类

问题可能原因排查方法解决方案
OOM内存泄漏/堆太小Heap Dump 分析修复泄漏/增加内存
CPU 100%死循环/大量计算top/Arthas优化代码
磁盘满日志太多/临时文件df -h日志轮转/清理
线程池耗尽慢操作占用线程Thread Dump异步化/扩容

17.6 故障演练清单

  定期故障演练(建议每月一次):

  ┌───────────────────────────────────────────────────────────┐
  │                    故障演练清单                            │
  ├───────────────────────────────────────────────────────────┤
  │                                                           │
  │  基础设施层:                                              │
  │  □ 杀死一个服务 Pod → 验证自动重启                        │
  │  □ 杀死数据库主节点 → 验证主从切换                         │
  │  □ 注入网络延迟 500ms → 验证超时和熔断                    │
  │  □ 注入网络分区 → 验证服务降级                            │
  │                                                           │
  │  应用层:                                                  │
  │  □ 模拟支付服务不可用 → 验证订单降级                      │
  │  □ 模拟缓存全部失效 → 验证缓存穿透保护                    │
  │  □ 模拟消息队列堆积 → 验证消费追赶能力                    │
  │  □ 模拟数据库慢查询 → 验证连接池和超时                     │
  │                                                           │
  │  业务层:                                                  │
  │  □ 模拟秒杀流量 (10x) → 验证限流和排队                    │
  │  □ 模拟重复下单 → 验证幂等性                              │
  │  □ 模拟部分支付失败 → 验证补偿机制                        │
  └───────────────────────────────────────────────────────────┘

17.7 事后复盘(Postmortem)

17.7.1 复盘模板

# 故障复盘报告

## 基本信息
- **故障时间**: 2026-05-10 10:00 - 11:30 (90 分钟)
- **影响范围**: 订单服务不可用,影响约 30% 用户
- **严重程度**: P1

## 故障时间线
| 时间 | 事件 |
|------|------|
| 10:00 | 监控告警:订单服务 P99 延迟飙升到 5s |
| 10:05 | 值班人员确认问题,开始排查 |
| 10:15 | 定位到数据库慢查询 |
| 10:30 | 确认是全表扫描导致 |
| 10:45 | 紧急添加索引,准备上线 |
| 11:00 | 索引上线完成 |
| 11:30 | 性能恢复正常,故障结束 |

## 根因分析
订单表新增字段后,原有查询未更新索引,导致全表扫描。

## 修复措施
1. 紧急:添加缺失索引
2. 长期:SQL 变更需经过 DBA Review

## 改进项
| 改进项 | 负责人 | 截止日期 |
|--------|--------|----------|
| 添加慢 SQL 监控告警 | 张三 | 2026-05-20 |
| SQL 变更需 DBA Review | 李四 | 2026-05-15 |
| 补充索引覆盖度测试 | 王五 | 2026-05-25 |

⚠️ 注意事项

  1. 不要在故障中做大的改动——故障期间只做最小修复
  2. 保持冷静,按流程排查——恐慌会导致更多错误
  3. 及时沟通——故障期间保持团队和业务方的沟通
  4. 事后复盘不要追责——聚焦于流程和系统的改进
  5. 建立 Runbook——常见故障的处理手册

📖 扩展阅读

  1. Google SRE Book — 故障管理的最佳实践
  2. Arthas Documentation — Java 在线诊断工具
  3. Resilience4j — 弹性通信框架
  4. Site Reliability Engineering — O’Reilly SRE 实践
  5. The Phoenix Project — IT 运维小说(理解故障管理的重要性)

本章小结

要点说明
调试方法RED 方法:复现 → 探索 → 诊断
核心工具TraceID 串联 + 链路追踪 + 日志聚合
性能排查从宏观指标到微观线程的逐层分析
熔断降级Resilience4j 实现熔断 + 降级策略
故障管理定期演练 + 事后复盘 + Runbook

📌 下一章第 18 章:最佳实践 — 架构演进、组织架构、反模式总结。