记录一次Flink作业异常的排查过程

本文记录了一次针对Flink作业异常的详细排查过程,从基础的日志分析到利用jstack和jmap深入诊断,最终定位到自建的EsClient线程泄漏问题。通过调整并发度和使用工具xland,发现并验证了EsClient在异常处理和资源关闭上的不足,强调了在Flink作业中正确管理外部客户端资源的重要性。

本文来自:HeapDump性能社区

PerfMa(笨马网络)官网

最近2周开始接手apache flink全链路监控数据的作业,包括指标统计,业务规则匹配等逻辑,计算结果实时写入elasticsearch. 昨天遇到生产环境有作业无法正常重启的问题,我负责对这个问题进行排查跟进。

第一步,基础排查

首先拿到jobmanager和taskmanager的日志,我从taskmanager日志中很快发现2个基础类型的报错,一个是npe,一个是索引找不到的异常

elasticsearch sinker在执行写入数据的前后提供回调接口让作业开发人员对异常或者成功写入进行处理,如果在处理异常过程中有异常抛出,那么框架会让该task失败,导致作业重启。

npe很容易修复,索引找不到是创建索引的服务中的一个小bug,这些都是小问题。

重点是在日志中我看到另一个错误:

java.lang.OutOfMemoryError: unable to create new native thread
	at java.lang.Thread.start0(Native Method)
	at java.lang.Thread.start(Unknown Source)
	at org.apache.flink.runtime.io.network.api.writer.RecordWriter.<init>(RecordWriter.java:122)
	at org.apache.flink.runtime.io.network.api.writer.RecordWriter.createRecordWriter(RecordWriter.java:321)
	at org.apache.flink.streaming.runtime.t
Flink 任务在重启后性能恢复正常,说明问题可能是**临时性状态、资源争用或状态膨胀**导致的。要排查这类问题,需要从多个维度分析:**状态管理、反压情况、Checkpoint/Savepoint 行为、资源使用、外部依赖等**。 下面是一个系统性的排查方案和对应的诊断方法: --- ### ✅ 1. 检查反压(Backpressure) 反压是 Flink 性能下降最常见的原因之一。如果某个算子处理速度跟不上输入速率,会导致上游积压数据,整体吞吐下降。 #### 🔍 排查方式: - 使用 Flink Web UI 查看 **"Back Pressure"** 页面(通常在 `http://<jobmanager>:8081/#/backpressure`)。 - 如果发现某些 task(如 keyBy 后的 operator)持续处于 HIGH 反压状态,则说明该算子是瓶颈。 #### 💡 常见原因: - 状态过大导致访问变慢(如 RocksDB 访问延迟高) - 外部系统写入慢(如 Kafka、数据库 I/O 延迟) - 用户代码中存在同步阻塞操作(如 Thread.sleep、同步 HTTP 调用) --- ### ✅ 2. 分析 Checkpoint 和状态大小 重启后性能恢复,很可能是由于 **状态膨胀(state bloat)** 导致性能逐渐恶化。 #### 🔍 排查方式: 查看 Flink Web UI 的 **Checkpoint 面板**: - 检查每个 checkpoint 的 **耗时和大小** - 是否随着时间推移,checkpoint 时间越来越长? - 是否出现超时或失败? ```bash # 示例输出(来自日志或 UI): Checkpoint 100: Size = 500MB, Duration = 15s Checkpoint 150: Size = 2GB, Duration = 60s → 明显增长! ``` #### 📌 可能原因: - Keyed State 无清理逻辑,导致状态无限增长(例如未清除 MapState 或 ValueState) - TTL(Time-to-Live)未设置 - Event Time 偏移太大,触发大量迟到数据缓存 #### ✅ 解决方案示例(Java/Scala):启用状态 TTL ```java import org.apache.flink.api.common.state.StateTtlConfig; import org.apache.flink.api.common.state.ValueStateDescriptor; import org.apache.flink.api.common.time.Time; // 设置状态 TTL:30分钟过期 StateTtlConfig ttlConfig = StateTtlConfig .newBuilder(Time.minutes(30)) .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite) .setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired) .cleanupFullSnapshot() // 注意:仅对全量快照有效 .build(); ValueStateDescriptor<String> descriptor = new ValueStateDescriptor<>("text", String.class); descriptor.enableTimeToLive(ttlConfig); ``` > 上述代码为状态启用了自动过期机制,防止状态无限增长。 --- ### ✅ 3. 查看 TaskManager 日志与 GC 日志 重启后性能恢复,也可能是 JVM **GC 压力过大**所致。 #### 🔍 排查方式: 开启并分析 GC 日志: ```bash # 启动参数建议添加: -XX:+PrintGCDetails \ -XX:+PrintGCDateStamps \ -Xloggc:/path/to/gc.log \ -XX:+UseG1GC ``` 然后使用工具如 [GCViewer](https://github.com/chewiebug/GCViewer) 分析日志。 #### ⚠️ 关注点: - Full GC 是否频繁?每次持续多久? - Old Gen 使用率是否持续上升直至 OOM? - STW(Stop-The-World)时间是否超过几秒? #### ✅ 建议调优: ```bash # 示例优化参数 -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=16m -Xms4g -Xmx4g ``` --- ### ✅ 4. 检查外部依赖延迟(I/O Bound) 比如 Sink 写 Kafka 很慢、异步请求响应延迟高等。 #### 🔍 排查方式: - 在用户代码中加入日志或 Metrics 统计耗时: ```java long start = System.currentTimeMillis(); collector.collect(transform(value)); long end = System.currentTimeMillis(); if (end - start > 100) { LOG.warn("Processing took {} ms", end - start); // 记录慢处理事件 } ``` - 使用 Flink 的内置指标:`numRecordsInPerSecond`, `numRecordsOutPerSecond` - 观察是否存在“断崖式”下降(开始正常,几天后骤降) --- ### ✅ 5. 检查 Watermark 和 Event Time 处理 长时间运行的任务如果 watermark 不前进,会导致窗口无法触发,状态堆积。 #### 🔍 排查方式: - 查看是否有算子显示 “Watermark: -922337233780...”(即 Long.MIN_VALUE),表示 watermark 卡住。 - 检查 `assignTimestampsAndWatermarks` 是否正确实现。 #### ✅ 正确做法(使用 BoundedOutOfOrderness): ```java env.addSource(new FlinkKafkaConsumer<>(...)) .assignTimestampsAndWatermarks( WatermarkStrategy .<String>forBoundedOutOfOrderness(Duration.ofSeconds(5)) .withTimestampAssigner((event, timestamp) -> extractTimestamp(event)) ); ``` --- ### ✅ 6. 使用 Metrics + Prometheus + Grafana 监控长期趋势 配置 Flink 将 metrics 推送到 Prometheus,建立监控大盘观察以下指标随时间的变化: | 指标 | 说明 | |------|------| | `numRestarts` | 若非零说明异常重启过 | | `lastCheckpointDuration` | 应稳定,不应持续增长 | | `currentInputBytesPerSecond` | 输入流量是否下降? | | `checkpointStateSize` | 状态大小是否线性增长? | | `jvm.memory.used` / `jvm.gc.time` | GC 是否成为瓶颈? | --- ### ✅ 7. 对比重启前后的资源配置一致性 有时候“恢复”只是因为重启时分配了更健康的 TM 或网络环境。 - 检查重启前后是否使用相同的 Slot 分配策略? - 是否有其他作业抢占资源? - 是否动态 scaling 导致分布不均? --- ## ✅ 总结:常见模式与根因 | 现象 | 可能原因 | 排查手段 | |------|---------|----------| | 重启后变快,之后又变慢 | 状态膨胀 | Checkpoint size 趋势图 | | CPU 利用率低但吞吐低 | 反压严重 | Web UI Backpressure 页面 | | 日志中频繁 GC | JVM 内存不足或碎片化 | GC 日志分析 | | Checkpoint 失败或超时 | 状态大 + 执行慢 | Checkpoint duration & size | | 输出延迟高 | 外部系统瓶颈 | 添加日志测速 | --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HeapDump性能社区

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值