根据这些想法我们思考得出几个比较关键点在于:
-
如何让上下游感知 Task Failed ?
-
如何清空下游不完整的数据 ?
-
Failover 完成后如何与上下游重新建立连接 ?
基于以上考虑我们决定基于已有的 Network 层线程模型,修改上下游对于 Task Failed 后的处理逻辑,让非故障的 Tasks 短时间内完成对失败 Task 的感知操作,从而使得作业持续稳定地输出。
当前架构
注:我们的实现基于 Flink-1.9,1.11 后的网络模型加入了 Unaligned Checkpoint 的特性,可能会有所变化。
我们先将 Flink 的上下游 Task 通信模型简单抽象一下:
Figure. 上下游通信模型
上下游 Task 感知彼此状态的逻辑,分三种情况考虑:
-
Task 因为逻辑错误或 OOM 等原因 Fail,Task 自身会主动释放 network resources,给上游发送 channel close 信息,给下游发送 Exception。
-
TaskManager 进程被 Yarn Kill,TCP 连接会被操作系统正常关闭,上游 Netty Server 和下游 Netty Client 可以感知到连接状态变化。
-
机器断电宕机,这个情况下操作系统不会正确关闭 TCP 连接,所以 Netty 的 Server 和 Client 可能互相感知不到,这个时候我们在 deploy 新的 Task 后需要做一些强制更新的处理。
可以看到,在大部分情况下,Task 是可以直接感知到上下游 Task 的状态变化。了解了基础的通信模型之后,我们可以按照之前的解决思路继续深入一下,分别在上游发送端和下游接收端可以做什么样改进来实现单点恢复。
优化方案
根据我们的解决思路,我们来绘制一下单个 Task 挂了之后,整个 Job 的通信流程:
Figure. 单点恢复流程
Map(1) 失败之后:
-
将 Map(1) 失败的信息通知 Source(1) 、Sink(1) 和 JobManager。
-
JobManager 开始申请新的资源准备 Failover,同时上游 Source(1) 和下游 Sink(1) 切断和 Map(1) 的数据通道,但是 Source(1) 和 Sink(1) 和其他 Task 的数据传输仍正常进行。
-
Map(1)’ 被成功调度,和上游建立连接,JobManager 通知 Sink(1) 和 Map