故障排查方法论
流处理故障排查遵循「从下游到上游,从外部到内部」:
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` 子命令)
常见错误对照表
| 错误 | 原因 | 解决 |
|---|---|---|
NoResourceAvailableException | Slot 不足 | 增加并行度或减少需求 |
ClassNotFoundException | 缺少依赖 | 检查 fat-jar |
TimeoutException | Checkpoint/网络 | 增加超时或检查反压 |
SerializationException | POJO 不规范 | 添加无参构造/getter/setter |
IllegalStateException | 多次调用 execute() | 一个 env 只能 execute() 一次 |
小结
| 故障 | 排查入口 | 常用解决 |
|---|---|---|
| 反压 | Web UI Backpressure | 增加并行度/减少状态 |
| OOM | 日志 / JMX | 增加内存/修复泄漏 |
| Checkpoint 超时 | Web UI Checkpoint | 增加超时/非对齐 Checkpoint |
| Watermark 滞后 | Metrics Watermark | idle timeout / 检查时间戳 |
| Rebalance | Kafka 日志 | 调整 max.poll.interval 与单批大小 |
下一节 14.3 从 v2 到 v3 的演进 :事件格式兼容、数据迁移、灰度切换。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。