Flink的容错机制第二篇 异步栅栏快照机制
上一篇讲到Flink是基于有状态的运算,而避免了传统流计算缺少程序状态支持的数据存储,访问,动态扩容,数据库回滚一致性等问题。这一篇主要讲Flink如何通过状态管理而做到流计算中的消息传输保障。
一,一致性与全局状态
在分布式系统中,运行着多个相互关联的服务节点。
一致性是指分布式系统中的多个服务节点,给定一系列的操作,在约定协议的保障下,使它们对外界呈现的状态是一致的。换句话说,也就是保证集群中所有服务节点中的数据完全相同并且能够对某个提案(Proposal)达成一致。
感兴趣的还可以了解一下CAP理论的强一致性和BASE理论的最终一致性。
在一些场景中,我们需要在分布式系统中获取它的全局状态,比如我们需要知道系统中是否达到了如死锁这样的稳定状态(fault tolerance),或者我们需要当前系统的版本信息来做系统优化(system reconfiguration)。这就像是在一群流动的羊中数羊的个数,解决方法就是给羊群拍一张照片,因此我们也把记录分布式系统全局状态的方法叫做快照(snapshot)。
二,流处理运算的三级消息传输语义
回到流计算概念,在流处理系统中,我们对应数据记录的处理,有3种级别的语义定义,以此来衡量这个流处理系统的能力。
At most once(最多一次)。每条数据记录最多被处理一次,含义就是,出现故障时,不保证这条消息原本应该涉及的所有处理节点计算都顺利完成。
At least once(最少一次)。每条数据记录至少被处理一次。含义就是,出现故障时,系统能够识别并进行tuple重发,但是没办法判断是否之前该元组被成功处理完成了,因此可能会有重复处理的情况,对于某些改变外部状态的场景,会造成脏数据。
Exactly once(恰好一次)。每条数据记录正好被处理一次。一条消息经过系统,不管是否发生故障,在所有处理节点上有且仅会被处理一次,这是最理想的情况,即使出现故障,也能符合正确的业务预期,但一般会带来比较大的性能开销。
相对于micro-batch底层实现的spark streaming,Apache Flink 便使用了分布式快照和检查点(checkpointing)机制来实现了exactly-once 的容错级别.
Flink 进行周期性的全局快照(periodic global state)保存,从而在出现系统failure的时候,只要从上一次保存成功的全局快照中恢复每个节点的恢复状态,然后再使源数据节点从相应快照标记源数据节点重新开始处理即可恢复无错运行状态(Kafka可以做到这一点).
三,Flink中的异步栅栏快照
同步快照有以下两种潜在的问题:
1. 需要所有节点停止工作,即暂停了整个计算,这个必然影响数据处理效率和时效性.
2. 需要保存所有节点/操作中的状态以及所有在传输中的数据(比如storm中的tuples),这个会消费大量的存储空间.
最原始解决异步快照问题的是Chandy lamport算法,算法总体上比较好理解,重点是通过传递marker来记录整条链路的全局状态。后续恢复的时候每个节点都可以从自己之前记录的checkpoint中恢复出来。
Flink基于Chandy lamport算法,制定了Flink自己应对流计算exactly-once语义的检查点机制Asynchronous Barrier Checkpointing
轻量级的异步栅栏快照可以用来为数据流引擎提供容错机制,同时减小的存储空间需求。因为ABS只需要保存一个无环拓扑中每个操作节点的处理状态(operator states).
异步栅栏快照的中心思想是在数据流中插入栅栏,并把event分割成不同组,通过切分源数据来划分阶段,每个集合的源数据也代表了其所需要的计算. 当上一个集合的输入数据以及输出都被完全处理,就代表上一个阶段结束了.
所以当一个阶段结束时,操作节点的状态可以代表这整个个运行历史,从而快照可以仅仅依赖于operator states.
这些阶段可以通过周期性的在源头出插入一些栅栏(barrier)来划分. 这些栅栏起到了阶段的标记作用,然后跟着数据流通过整个数据处理pipeline,知道系统的sinks.
全局状态在这个过程中,被增量地构建, 即当每个处理tast接收到对应id的栅栏的时候对自己的状态进行快照,而每个节点异步的快照共同组成了一个阶段(stage)。
具体每个operator上,分为4个步骤:
1.对齐预备:当operator收到某个输入流中的一个barrier,就暂停运算当前输入流的数据直到这个operator收到所有来自输入流的barrier。
2. 数据对齐:暂停运算的输入流传来的数据会被保存在operator的缓存中,等待运算。
3. 制作检查点:operator收到所有的barrier时,开始上传状态制作检查点。
4. 继续运算:operator会先运算缓存中存储的数据,再继续运算流中的数据
当数据sinks收到所有barriers并且进行自身状态保存之后,也进行ack的checkpointing.