我们知道Flink提供了容错机制,能够在应用失败的时候重新恢复任务。这个机制主要就是通过持续产生快照的方式实现的。Flink快照主要包括两部分数据一部分是数据流的数据,另一部分是operator的状态数据。对应的快照机制的实现有主要两个部分组成,一个是屏障(Barrier),一个是状态(State)。因为Flink这里处理的数据流,数据在多个operator的DAG拓扑中持续流动,要想实现某个时刻快照可以用于系统故障恢复,必须保证这个快照,完全能够确定某一个时刻状态,这个时刻之前的数据全部处理完,之后的数据一个都没有处理。这里就引入了屏障这个概念。这里我们主要介绍一下屏障实现。
屏障 Barrier
Flink 分布式快照里面的一个核心的元素就是流屏障(stream barrier)。这些屏障会被插入(injected)到数据流中,并作为数据流的一部分随着数据流动。屏障并不会持有任何数据,而是和数据一样线性的流动。可以看到屏障将数据流分成了两部分数据(实际上是多个连续的部分),一部分是当前快照的数据,一部分下一个快照的数据。每个屏障会带有它的快照ID。这个快照的数据都在这个屏障的前面。从图上看,数据是从左向右移动(右边的先进入系统),那么快照n包含的数据就是右侧到下一个屏障(n-1)截止的数据,图中两个灰色竖线之间的部分,也就是part of checkpoint n。另外屏障并不会打断数的流动,因而屏障是非常轻量的。在同一个时刻,多个快照可以在同一个数据流中,这也就是说多个快照可以同时产生。
如果是多个输入数据流,多个数据流的屏障会被同时插入到数据流中。快照n的屏障被插入到数据流的点(我们称之为Sn),就是数据流中一直到的某个位置(包含了当前时刻之前时间的所有数据),也就是包含的这部分数据的快照。举例来说,在Kafka中,这个位置就是这个分区的最后一条记录的offset。这个位置Sn就会上报给 checkpoint 的协调器(Flink的 JobManager)。
然后屏障开始向下流动。当一个中间的operator收到它的所有输入源的快照n屏障后,它就会向它所有的输出流发射一个快照n的屏障,一旦一个sink的operator收到所有输入数据流的屏障n,它就会向checkpoint的协调器发送快照n确认。当所有的sink都确认了快照n,系统才认为当前快照的数据已经完成。
一旦快照n已经执行完成,任务则不会再请求Sn之前的数据,因为此刻,这些数据都已经完全通过了数据流拓扑图。
对齐机制
接收不止一个数据输入的operator需要基于屏障对齐输入数据流。详述如下:
整个流程图如下所示
然后我们挨个看一下:
-
当operator接收到快照的屏障n后并不能直接处理之后的数据,而是需要等待其他输入快照的屏障n。否则话,将会将快照n的数据和快照n+1的数据混在一起。图中第一个所示,operator即将要收到数据流1(上面这个我们当成数据流1(6,5,4,3,2,1),下面的当成数据流2好了)的屏障n,1,2,3在屏障n之后到达operator,这个时候如果数据流1的继续处理,那么operator中就会包含n屏障之后的数据(1,2,3),但是operator中此刻在接收和处理数据流2,数据(a,b,c)就会和数据流1中的(1,2,3)混合。
- 快照n的数据流会被暂时放到一边。从这些数据流中获取到的数据不会被处理,而是存储到一个缓冲中。图中第一个所示,因为数据流2的屏障n还没到,所以operator持续接收1,2,3然而并不做任何处理。但是需要将1,2,3存入到buffer中。此时第二个数据流接到a,b,则直接发送,接到c发送c。
- 一旦最后一个数据流收到了快照n,opertor就会将发出所有阻塞的数据,并发出自己的屏障。如图中第三个所示,operator最后收到了另一个数据流的屏障n,然后再发出a,b,c(图中operator中的c,b,a)以后,发出自己的屏障,这个时候buffer中又增加了一个4,变成(4,3,2,1)。
- 之后operator会重新开始处理所有的输入数据流,先处理buffer中的数据,处理完之后再处理输入数据流的数据。如图第四个所示,先将buffer中的1,2,3,4先处理完,在接收并处理这两个数据源的数据。
··=-·=···············