深入浅出Flink容错机制
概述
Apache Flink提供容错机制,以持续恢复数据流应用程序的状态。该机制确保即使存在故障,程序的状态最终也只能反映数据流中的每条记录一次。请注意,有一个开关将保证降级至少一次(如下所述)。
容错机制连续地绘制分布式流数据流的SNAPSHOT。对于状态较小的流应用程序,这些SNAPSHOT非常轻量级,可以经常绘制,而不会对性能产生太大影响。流应用程序的状态存储在可配置的位置(例如主节点或HDFS)。
如果程序失败(由于机器,网络或软件故障),Flink将停止分布式流数据流。然后,系统重新启动算子并将其重置为最新的成功检查点。输入流将重置为状态SNAPSHOT的点。作为重新启动的并行数据流的一部分处理的任何记录都保证不会成为先前检查点状态的一部分。
注意:默认情况下,禁用检查点。有关如何启用和配置检查点的详细信息,请参阅检查点。
注意:要使此机制实现其完全保证,数据流源(例如消息队列或代理)需要能够将流回滚到定义的最近点。Apache Kafka具有这种能力,Flink与Kafka的连接器利用了这种能力。有关Flink连接器提供的保证的更多信息,请参阅数据源和接收器的容错保证。
注意:由于Flink的检查点是通过分布式SNAPSHOT实现的,因此我们可以互换使用SNAPSHOT和检查点。
Checkpoint
Flink的容错机制的核心部分是绘制分布式数据流和算子状态的一致SNAPSHOT。这些SNAPSHOT充当一致的检查点,系统可以在发生故障时退回。Flink用于绘制这些SNAPSHOT的机制在“分布式数据流的轻量级异步SNAPSHOT”中进行了描述。它受到分布式SNAPSHOT的标准Chandy-Lamport算法的启发,专门针对Flink的执行模型而定制。
障碍
Flink分布式SNAPSHOT的核心数据元是流障碍。这些障碍被注入数据流并与记录一起作为数据流的一部分流动。障碍永远不会超过记录,流量严格符合要求。屏障将数据流中的记录分为进入当前SNAPSHOT的记录集和进入下一个SNAPSHOT的记录。每个屏障都带有SNAPSHOT的ID,该SNAPSHOT的记录在其前面推送。障碍不会中断流的流动,因此非常轻。来自不同SNAPSHOT的多个障碍可以同时在流中,这意味着可以同时发生各种SNAPSHOT。
流障碍被注入流源的并行数据流中。注入SNAPSHOTn的障碍(我们称之为Sn)的点是源流中SNAPSHOT覆盖数据的位置。例如,在Apache Kafka中,此位置将是分区中最后一条记录的偏移量。该位置S n被报告给检查点协调员(Flink的JobManager)。
然后障碍物向下游流动。当中间算子从其所有输入流中收到SNAPSHOTn的屏障时,它会为其所有输出流中的SNAPSHOTn发出屏障。一旦接收器算子(流式DAG的末端)从其所有输入流接收到障碍n,它就向SNAPSHOTn确认检查点协调器。在所有接收器确认SNAPSHOT之后,它被认为已完成。
一旦完成SNAPSHOTn,作业将永远不再向源请求来自Sn之前的记录,因为此时这些记录(及其后代记录)将通过整个数据流拓扑。
接收多个输入流的 算子需要在SNAPSHOT屏障上对齐输入流。上图说明了这一点:
- 一旦算子从输入流接收到SNAPSHOT屏障n,它就不能处理来自该流的任何其他记录,直到它从其他输入接收到屏障n为止。否则,它会混合属于SNAPSHOTn的记录和属于SNAPSHOTn + 1的记录。
- 报告障碍n的流暂时被搁置。从这些流接收的记录不会被处理,而是放入输入缓冲区。
- 一旦最后一个流接收到屏障n,算子就会发出所有挂起的传出记录,然后自己发出SNAPSHOTn个屏障。
- 之后,它恢复处理来自所有输入流的记录,在处理来自流的记录之前处理来自输入缓冲区的记录。
状态
当算子包含任何形式的状态时,此状态也必须是SNAPSHOT的一部分。算子状态有不同的形式:
- 用户定义的状态:这是由转换函数(如map()或filter())直接创建和修改的状态。有关详细信息,请参阅Streaming Applications中的State。
- 系统状态:此状态是指作为算子计算一部分的数据缓冲区。此状态的典型示例是窗口缓冲区,系统在其中收集(和聚合)窗口记录,直到窗口被评估和逐出。
算子在他们从输入流接收到所有SNAPSHOT障碍时,以及在向其输出流发出障碍之前对其状态进行SNAPSHOT。此时,将根据障碍之前的记录对状态进行所有更新,并且在应用障碍之后不依赖于记录的更新。由于SNAPSHOT的状态可能很大,因此它存储在可配置的状态后台中。默认情况下,这是JobManager的内存,但对于生产使用,应配置分布式可靠存储(例如HDFS)。在存储状态之后,算子确认检查点,将SNAPSHOT屏障发送到输出流中,然后继续。
生成的SNAPSHOT现在包含:
- 对于每个并行流数据源,启动SNAPSHOT时流中的偏移/位置
- 对于每个算子,指向作为SNAPSHOT的一部分存储的状态的指针
完全一次与至少一次
对齐步骤可以增加流式传输程序的等待时间。通常,这种额外的延迟大约为几毫秒,但我们已经看到一些异常值的延迟显着增加的情况。对于要求所有记录始终具有超低延迟(几毫秒)的应用程序,Flink可以在检查点期间跳过流对齐。一旦算子看到每个输入的检查点障碍,仍然会绘制检查点SNAPSHOT。
当跳过对齐时,即使在检查点n的某些检查点障碍到达之后,算子仍继续处理所有输入。这样,算子还可以在获取检查点n的状态SNAPSHOT之前处理属于检查点n+1的数据元。在还原时,这些记录将作为重复记录出现,因为它们都包含在检查点n的状态SNAPSHOT中,并将在检查点n之后作为数据的一部分进行重放。
注意:对齐仅适用于具有多个前驱(连接)的 算子以及具有多个发送方的 算子(在流重新分区/随机播放之后)。正因为如此,数据流只有尴尬的并行流 算子操作(map(),flatMap(),filter(),…)实际上给正好一次保证了即使在至少一次模式。
异步状态SNAPSHOT
注意,上述机制意味着算子在将状态的SNAPSHOT存储在状态后台时停止处理输入记录。每次拍摄SNAPSHOT时,此同步状态SNAPSHOT都会引入延迟。
可以让算子在存储状态SNAPSHOT时继续处理,有效地让状态SNAPSHOT在后台异步发生。为此,算子必须能够生成一个状态对象,该状态对象应以某种方式存储,以便对算子状态的进一步修改不会影响该状态对象。例如,诸如RocksDB中使用的写时复制数据结构具有这种行为。
在接收到输入的检查点障碍后,算子启动其状态的异步SNAPSHOT复制。它立即释放其输出的障碍,并继续进行常规流处理。后台复制过程完成后,它会向检查点协调员(JobManager)确认检查点。检查点现在仅在所有接收器都已收到障碍并且所有有状态算子已确认其完成备份(可能在障碍物到达接收器之后)之后才完成。
有关状态SNAPSHOT的详细信息,请参阅状态后台。
复苏
在这种机制下的恢复是直截了当的:当失败时,Flink选择最新完成的检查点k。然后,系统重新部署整个分布式数据流,并为每个算子提供作为检查点k的一部分进行SNAPSHOT的状态。设置源以开始从位置S k读取流。例如,在Apache Kafka中,这意味着告诉消费者从偏移量S k开始提取。
如果状态以递增方式SNAPSHOT,则算子从最新完整SNAPSHOT的状态开始,然后对该状态应用一系列增量SNAPSHOT更新。
有关更多信息,请参阅重启策略
算子SNAPSHOT实施
在执行算子SNAPSHOT时,有两部分:同步和异步部分。
算子和状态后台将其SNAPSHOT作为Java提供FutureTask。该任务包含完成同步部分且异步部分处于挂起状态的状态。然后,异步部分由该检查点的后台线程执行。
检查点纯粹同步返回已经完成的算子FutureTask。如果需要执行异步 算子操作,则以该run()方法执行FutureTask。
任务是可取消的,因此可以释放流和其他资源消耗句柄。