Flink checkpoint 算法(上)

政采云技术团队.png

熠然.png

前言

Apache Flink is a framework and distributed processing engine for stateful computations over unbounded and bounded data streams.

        Apache Flink 是一个对无界数据流或有界数据流进行有状态计算的分布式处理引擎和框架,可以用来构建有状态的分布式数据流处理系统。而对于有状态的分布式系统,当系统因某些原因如硬件故障、网络问题或程序进程被杀等,导致系统运行失败,此时我们希望可以对系统进行失败恢复(Failure Recovery),恢复后的系统应该接着系统失败前的状态继续处理而不是从头开始计算,那么我们需要在系统失败前记录系统的全局状态以便系统在失败恢复时使用。

        但是对于分布式系统而言,在一个时间点记录下系统的全局状态并不容易,因为我们缺乏一个可靠的全局时钟及全局的共享内存,并且系统内节点间的通信延迟也是不可预知的。

那么如何记录系统的全局状态呢?我们先看一个自然且易理解的算法:

  1. 当系统需要记录状态时,首先暂停系统的外部数据输入;

  2. 等待系统内部所有节点完成正在处理及等待处理的数据;

  3. 记录系统内部各个节点的状态,组成系统全局状态;

  4. 全局状态记录完成后,恢复系统外部数据输入,同时各节点恢复计算。

        这是一个同步记录全局状态的算法,且需要"Stop The World",显然这个算法会严重影响系统的吞吐量,并不是一个好的方案。Flink checkpoint 没有采用这种算法,它的算法叫做Asynchronous Barrier Snapshot(ABS),是系统全局快照算法 Chandy-Lamport 算法的变种,因此在讲解 Flink checkpoint 算法之前,我们先来介绍 Chandy-Lamport 算法。

Chandy-Lamport 算法

        Chandy-Lamport 是一个记录分布式系统状态的快照算法,由 K. Mani ChandyLeslie Lamport 共同发明。

分布式系统模型

我们先定义一个简化的分布式系统模型:

一个分布式系统由有限个处理节点,我们称为 Process,Process 可以分布在不同的物理机上,以及各个 Process 之间通信的通道 Channel 组成,Channel 是有向的。

可以通过一个有向图来展示:

  1. P1, P2, P3 表示分布式系统中的节点;

  2. C12 表示 P1 发送消息到 P2 的通道,称为 P1 的 outgoing 通道,P2 的 incoming 通道;

  3. C21 表示 P2 发送消息到 P1 的通道,称为 P2 的 outgoing 通道,P1 的 incoming 通道。

算法假定如下条件:

  1. 所有消息 Message 在 channel 中的传输是有序的,遵循 FIFO 原则;

  2. 所有 Message 传输可以延迟,但是最终都能到达 Process;

  3. Process 不会崩溃。

算法规则

        一个分布式系统的全局快照由各个 Process 的本地状态和各个 Channel 的状态组成,那么 Process 和 Channel 的状态如何记录呢?

        当分布式系统运行时,Process 通过 Channel 发送和接收消息,伴随消息的发送、接收、处理,Process 和 Channel的状态的状态会随之而变。Chandy-Lamport 算法加入了一个特殊的消息,称为 Marker 消息,用来触发对 Process 和 Channel 的状态进行快照。

Process 发送和收到 Marker 消息的行为规则如下:

Marker 消息发送者规则:

  • 首先,Process 记录本地状态;

  • 然后,Process 延所有 outgoing 通道发送 Marker 消息;

  • 最后,Process 记录除接收到 Marker 消息的通道外的所有 incoming 通道的消息。

Marker 消息接收者规则,分两种情况:

情况一、Process 第一次收到本次快照的 Marker 消息

  • Process 将收到 Marker 消息的 incoming 通道状态设置为空;

  • Process 根据发送者规则继续后续处理。

情况二、Process 已经收到过本次快照的 Marker 消息

  • 停止记录接收 Marker 消息的通道的消息;

  • 将该通道的状态设置为 Process 记录本地状态后开始记录的该通道的消息序列。

由上可知,Marker 消息逻辑上分隔了本次快照和下次快照的消息。

当所有进程都从所有 incoming 通道收到 Marker 后,算法结束。

算法流程

算法可以由系统中的任一个 Process 发起,流程如下:

1、发起快照

假设由 Process Pi发起快照

  • Pi 记录本地状态

  • Pi 对其所有 outgoing 通道发送 Marker 消息

  • Pi 记录所有其 incoming 通道的消息

2、传播快照

对于 Process Pj,其从 Cij 通道接收到 Marker 消息

如果是首次接收到 Marker 消息

  • Pj 记录自己的本地快照

  • Pj 将通道 Cij 的状态设置为空

  • Pj 对其所有 outgoing 通道发送 Marker 消息

  • Pj 开始记录除 Cij 外的所有 incoming 通道的消息

如果不是首次接收到 Marker 消息

  • Pj 停止记录从 Cij 通道接收的消息

  • Cij 通道的状态为:Pj 首次收到其他 incoming 通道的 Marker 消息触发记录开始到停止,这段时间内的消息序列。

3、结束快照

        当所有 Process 从所有的 incoming 通道都收到了 Marker 消息,记录 Process 本地状态和其所有 incoming 通道的状态后,快照完成。

        每个 Process 都可以记录自己的本地状态和它的 incoming 通道状态,当所有的 Process都记录完状态后,我们需要一个外部的协调程序从每个 Process收集状态数据,然后形成一个系统完整的状态。

算法示例

我们假设系统中有 P1、P2、P3 三个进程,C12、C21、C23、C31 四个通道。

1、系统的初始状态,这里我们使用数据的集合作为状态值。

Process 状态为:

P1 = {a, b, c}            P2 = {d, e, f}            P3 = {g, h, i}

Channel 状态为:

C12 = {}                    C21 = {}                   C23 = {}                C31 = {}

2、系统运行后,假设 P1 通过 C12 给 P2 发送了消息 c,而 P2 通过 C21 给 P1 发送了消息 d、通过 C23 给 P3 发送了消息 e,P3 通过 C31 给 P1 发送了消息 g(下图左)。

此时我们从 P1 开始发起快照(下图右):

  • P1 记录自己的状态为 {a, b}

  • P1 通过 outgoing 通道 C12 发送 Marker 消息 M 到 P2

  • P1 开始记录从 incoming 通道 C21、C31 收到的消息

这一阶段我们记录了:

Process 状态为:

P1 = {a, b}

3、(下图右)P2 从 C12 先后(FIFO)收到消息 c 和 Marker 消息 M,此为 P2 第一次收到 Marker 消息:

  • P2 记录本地状态为 {c, f}

  • 将通道 C12 的状态设置为空

  • P2 通过 outgoing 通道 C21、C23 分别发送 Marker 消息到 P1、P3

  • P2 除 C12 外没有其他 incoming 通道,不需要再记录 incoming 通道的消息

P1 开始记录从 incoming 通道 C21、C31 收到的消息后,记录从 C21 收到消息 d,记录从 C31 收到的消息 g。

此时 P3 继续发送消息 h 到 P1

到这一阶段,我们记录了:

Process 状态为:

P1 = {a, b}            P2 = {c, f}          

Channel 状态为:

C12 = {}                

记录的 Channel 消息为:

C21 = {d}              C31 = {g}

3、(下图右)P1 从 C21 收到 P2 发来的 Maker 消息 M,因 P1 不是第一次遇到 Marker 消息,P1 此时的行为:

  • 停止记录从 C21 接收到的消息

  • 将之前记录的消息 d 作为通道 C21 的状态

P3 从 C23 收到 P2 发来的 Maker 消息 M,P3 是第一次收到 Marker 消息,P3 此时的行为:

  • 记录 P3 的本地状态为 {i, e}

  • 将通道 C23 状态置为空

  • P3 对所有 outgoing 通道(此时只有 C31)发送 Marker 消息

  • P3 记录除 C23 的 incoming 通道的消息(没有其他 incoming 通道)

P1 继续记录从 C31 收到的消息 h。

到这一阶段,我们记录了:

Process 状态为:

P1 = {a, b}            P2 = {c, f}          P3 = {i, e} 

Channel 状态为:

C12 = {}          C21 = {d}               C23 = {}

记录的 Channel 消息为:             

C31 = {g, h}

4、(下图右)P1 从 C31 收到 P3 发来的 Maker 消息 M,因 P1 不是第一次遇到 Marker 消息,P1 此时的行为:

  • 停止记录从 C31 接收到的消息

  • 将之前记录的消息 f, g 消息序列作为通道 C31 的状态

至此,我们记录了:

Process 状态为:

P1 = {a, b}            P2 = {c, f}          P3 = {i, e} 

Channel 状态为:

C12 = {}          C21 = {d}        C23 = {}             C31 = {g, h}

总结

我们看到 Chandy-Lamport 算法是一个简单而有效的分布式系统快照算法,它不会要求停止发送消息更不会要求停止整个系统,通过 Chandy-Lamport 算法记录下来的全局状态和任何一个确切时间点的系统状态是不一致的,而是提供了一个逻辑上一致的系统状态快照。

参考

lamport.azurewebsites.net/pubs/chandy…

Distributed Snapshots: Determining Global States of Distributed Systems | the morning paper

Paper 阅读: Distributed Snapshots: Determining Global States of Distributed Systems | Matt's Blog

推荐阅读

从线上死锁分析到 Next-Key Lock 理解

深入理解 MyBatis

Linux 是如何启动的

ElasticSearch 磁盘 io 瓶颈问题解决方案探索

招贤纳士

政采云技术团队(Zero),一个富有激情、创造力和执行力的团队,Base 在风景如画的杭州。团队现有300多名研发小伙伴,既有来自阿里、华为、网易的“老”兵,也有来自浙大、中科大、杭电等校的新人。团队在日常业务开发之外,还分别在云原生、区块链、人工智能、低代码平台、中间件、大数据、物料体系、工程平台、性能体验、可视化等领域进行技术探索和实践,推动并落地了一系列的内部技术产品,持续探索技术的新边界。此外,团队还纷纷投身社区建设,目前已经是 google flutter、scikit-learn、Apache Dubbo、Apache Rocketmq、Apache Pulsar、CNCF Dapr、Apache DolphinScheduler、alibaba Seata 等众多优秀开源社区的贡献者。如果你想改变一直被事折腾,希望开始折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊……如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的技术团队的成长过程,我觉得我们该聊聊。任何时间,等着你写点什么,发给 [email protected]

微信公众号

文章同步发布,政采云技术团队公众号,欢迎关注

政采云技术团队.png

猜你喜欢

转载自juejin.im/post/7127062037686911006