14.2 故障排查

eventd v3 常见故障排查手册:反压原因定位(算子级别→网络→JVM)、状态过大导致的 OOM 与 Checkpoint 超时、Watermark 长期滞后的排查步骤、Kafka Consumer Group 再均衡与 offset 丢失场景的诊断方法。

故障排查方法论

流处理故障排查遵循「从下游到上游,从外部到内部」:

1. 面板是否有数据?
   → 否 → Redis 有数据?
     → 否 → Flink 作业运行?
       → 否 → 看 Flink 日志
       → 是 → Watermark 推进?
     → 是 → SSE 连接?
   → 是 → 数据正确?
     → 否 → 清洗管道正常?
       → 否 → TopologyTestDriver 测试
       → 是 → Flink 聚合逻辑?

反压排查

现象

Web UI 显示某算子 BACK_PRESSURED(红色)。

排查步骤

Step 1: 找到瓶颈算子
    Web UI → Job Graph → 从 Source 往下找最后一个红色的算子
    → 红色表示「输出被下游堵住」,瓶颈在它的下游
    → Sink 通常不显示红色,要单独看 records-in/out 与外部系统

Step 2: 检查瓶颈算子的指标
    records-in-per-second vs records-out-per-second
    → 如果 out << in → 算子处理慢

Step 3: 看瓶颈算子的状态大小
    Checkpoint page → 该算子状态 > 100MB?
    → 大状态 → RocksDB 调优

Step 4: 看 JVM GC
    GC 暂停与基线相比是否明显变长?(不要用一个固定毫秒数当门槛)
    → 调整堆内存或 GC 策略

Step 5: 看网络缓冲
    backPressuredTimeMsPerSecond > 0?
    → 增加 taskmanager.memory.network

解决方案

原因解决
算子逻辑太慢简化逻辑、减少状态访问、用异步 I/O
并行度不够增加并行度(≤ Kafka partition 数)
状态太大设置 TTL、增量 Checkpoint、RocksDB 调优
GC 频繁增加堆内存、用 G1GC、减少对象创建
网络瓶颈增加 network buffer、分布式部署

OOM(OutOfMemoryError)

现象

TaskManager 崩溃,日志中出现 java.lang.OutOfMemoryError。

排查

1. 堆 OOM
   → 增加 taskmanager.memory.process.size
   → 检查是否有未关闭的 Redis/Kafka 连接(泄漏)

2. 元空间 OOM
   → 增加 -XX:MaxMetaspaceSize
   → 检查是否有类加载泄漏

3. 直接内存 OOM
   → Network buffer 过大
   → 减少 taskmanager.memory.network.fraction

eventd v3 的内存配置

# 4GB 总内存分配
taskmanager.memory.process.size: 4096m
taskmanager.memory.jvm-overhead.fraction: 0.15  # JVM 开销 ≈ 614MB
taskmanager.memory.managed.fraction: 0.30       # RocksDB ≈ 1045MB
taskmanager.memory.network.fraction: 0.15       # 网络缓冲 ≈ 522MB

这些 fraction 的分母不一样,直接把比例相加会算错:

Total Process Memory 4096m
├── JVM Overhead        0.15 × 4096      ≈ 614m
└── Total Flink Memory  (4096 - 614)     ≈ 3482m
    ├── Managed(RocksDB) 0.30 × 3482    ≈ 1045m
    ├── Network            0.15 × 3482    ≈ 522m
    ├── Framework heap/off-heap           ≈ 256m(默认各 128m)
    └── Task Heap          余下的部分      ≈ 1659m

jvm-overhead.fraction 的分母是进程总内存,而 managed/network 的分母是 Total Flink Memory。另外 Flink 1.20 没有 taskmanager.memory.jvm-heap.fraction 这个键:任务堆内存由 taskmanager.memory.task.heap.size 指定,或者像上面这样由总内存减掉其他部分自动推导。网上不少配置片段用的是不存在的键,多数运行模式下不会因此启动失败,只是配置不生效——调完内存一定要在 Web UI 的 TaskManager 页面核对实际分配值。

Checkpoint 超时

现象

日志:Checkpoint expired before completing。

原因

1. 状态太大 → Checkpoint 时间 > timeout
2. 反压 → Barrier 等待太久
3. 外部存储慢 → HDFS/S3 写入慢
4. 网络慢 → TM 之间 Barrier 传递慢

解决

// 增加超时时间
env.getCheckpointConfig().setCheckpointTimeout(120000);  // 120s

// 启用非对齐 Checkpoint(反压场景)
env.getCheckpointConfig().enableUnalignedCheckpoints();

// 减少状态大小
env.getCheckpointConfig().setMinPauseBetweenCheckpoints(30000);

Watermark 滞后

现象

currentOutputWatermark 远小于当前系统时间,窗口永远不触发。

原因

1. 某个 Kafka partition 无数据 → Watermark 不推进
2. Watermark 策略的延迟设太大 → 故意等待
3. 事件时间戳错误 → 使用了 Processing Time

排查

// 检查各 Source subtask 的 Watermark
// Flink Web UI → Source → Metrics → currentOutputWatermark

// 启用 idle timeout
WatermarkStrategy.<Event>forBoundedOutOfOrderness(Duration.ofSeconds(10))
    .withIdleness(Duration.ofMinutes(5));  // 5 分钟无数据 = idle

Kafka Consumer Group 再均衡

现象

Kafka Streams 或 Flink Kafka Source 频繁 rebalancing:

[Consumer clientId=..., groupId=...] Joining group
[Consumer clientId=..., groupId=...] Rebalance started

原因

1. 消费太慢 → Consumer 心跳超时 → Coordinator 踢出
2. 实例频繁上下线 → 触发再均衡
3. Consumer 过多(> partition 数)→ 部分 Consumer 空闲

解决

// 心跳间隔要保持小于 session 超时(经验值是 1/3 左右);
// 把 session.timeout 调到比默认 45s 还短,只会让 rebalance 更频繁
props.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, 3000);   // 默认 3s
props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 45_000);    // 默认 45s

// 「处理一批超过 max.poll.interval.ms 被踢出组」比心跳超时更常见:
// 要么让每批更小,要么给处理留更多时间
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 500);
props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 300_000);

死链排查

1. 作业状态:RUNNING?
   → flink list
   → Web UI http://jobmanager:8081

2. Kafka lag 增长?
   → kafka-consumer-groups.sh --bootstrap-server kafka:9092 --describe --group eventd-analytics
   → 如果 lag > 0 → 消费太慢

3. Redis 连接正常?
   → redis-cli ping
   → 检查 TTL 是否已过期

4. 检查日志
   → Web UI 的 TaskManager/JobManager 日志页
   → $FLINK_HOME/log/flink-*-taskmanager-*.log
   → 容器部署:docker logs <container>
   (Flink CLI 没有 `flink logs` 子命令)

常见错误对照表

错误原因解决
NoResourceAvailableExceptionSlot 不足增加并行度或减少需求
ClassNotFoundException缺少依赖检查 fat-jar
TimeoutExceptionCheckpoint/网络增加超时或检查反压
SerializationExceptionPOJO 不规范添加无参构造/getter/setter
IllegalStateException多次调用 execute()一个 env 只能 execute() 一次

小结

故障排查入口常用解决
反压Web UI Backpressure增加并行度/减少状态
OOM日志 / JMX增加内存/修复泄漏
Checkpoint 超时Web UI Checkpoint增加超时/非对齐 Checkpoint
Watermark 滞后Metrics Watermarkidle timeout / 检查时间戳
RebalanceKafka 日志调整 max.poll.interval 与单批大小

下一节 14.3 从 v2 到 v3 的演进 :事件格式兼容、数据迁移、灰度切换。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「java」更多文章

  1. 15.3 全书总结与学习路径回顾
  2. 15.2 响应式流处理的演进趋势
  3. 15.1 引擎选型指南