Apache Flink - 数据流容错机制

Apache Flink提供了一种容错机制,可以持续恢复数据流应用程序的状态。该机制确保即使出现故障,程序的状态最终也会反映来自数据流的每条记录(只有一次)。

从容错和消息处理的语义上(at least once, exactly once),Flink引入了state和checkpoint。

state一般指一个具体的task/operator的状态。而checkpoint则表示了一个Flink Job,在一个特定时刻的一份全局状态快照,即包含了所有task/operator的状态。

Flink通过定期地做checkpoint来实现容错和恢复,容错机制连续绘制了分布式流数据流的快照。对于小状态的流应用程序,这些快照非常轻量级并且可以经常绘制,而不会对性能产生太大的影响。流应用程序的状态存储在一个可配置的地方(例如主节点或HDFS)。

如果出现程序故障(由于机器、网络或软件故障),Flink将停止分布式流数据流。然后系统重新启动操作符并将其重新设置为最新成功的检查点。输入流被重置到状态快照的点。默认情况下,检查点是禁用的。

要使此机制实现其全部的保证,数据流源(如消息队列或代理)需要能够将流倒回到其定义的最近点。Apache Kafka可以做到,而Flink的Kafka连接器可以利用这些。

因为Flink通过分布式检查点实现快照,我们使用快照和检查点互换。

checkpointing:

  • 检查点默认情况下不被保留,并且仅用于从失败中恢复作业。当程序被取消时,检查点被删除,你可以配置定期的检查点使他们得以保留。
  • Flink容错机制的核心部分是绘制分布式数据流和操作符状态的一致的快照。这些快照充当一致的检查点,在出现故障时系统可以退回到检查点。
  • Barriers:Flink的分布式快照的核心元素是stream barriers。这些barriers被注入到数据流中和记录一样作为数据流的一部分流动。Barriers从不会超过记录。Barriers将数据流中的记录分为进入当前快照的记录集和进入下一个快照的记录。每个barriers都带有快照的ID,该快照的记录在其前面推送。Barriers不会阻断流的流动。                                                               流barriers被注入到流数据源的并行数据流中,快照n的barriers(我们称之为Sn)被注入的点是源流中快照覆盖数据的位置。例如,在Apache Kafka中,此位置是分区中最后一条记录的偏移量。该位置Sn被报告给Flink的JobManager。然后barriers继续流动,当中间操作符从其所有输入流都收到快照n的barriers时,他会向所有输出流发出(emit)快照n的barriers。一旦操作符接收器(流DAG的末端)从它的所有输入流接收到barrier n,它就向快照n确认检查点协调器。在所有接收器确认快照后,它被视为已完成。一旦完成快照n,作业将永远不再向源请求来自Sn之前的记录,因为此时这些记录(及其后代记录)将通过整个数据流拓扑。 接收多个输入流的运算符需要在快照barriers上对齐输入流。上图说明了这一点:
    • 一旦操作员从输入流接收到快照barriers n,它就不能处理来自该流的任何其他记录(而是缓存),直到它从其他输入接收到barrier n为止否则它会混合属于快照n和属于快照n + 1的记录。(begin aligning - aligning)
    • 报告barrier n的流暂时被搁置。从这些流接收的记录不会被处理,而是放入输入缓冲区。(aligning)
    • 一旦最后一个输入流接收到barrier n,操作符就会发出所有挂起的传出记录,然后自己发出快照n的barriers。(checkpoint - continue)
    • 之后,它恢复处理来自所有输入流的记录,在处理来自流的记录之前处理来自输入缓冲区的记录。(continue)
  • State:当运算符包含任何形式的状态时,此状态也必须是快照的一部分。运算符状态有不同的形式:
    • 用户定义的状态:这是由转换函数(如map()filter())直接创建和修改的状态
    • 系统状态:此状态是指作为运算符计算一部分的数据缓冲区。此状态的典型示例是窗口缓冲区,系统在其中收集(和聚合)窗口记录,直到窗口被评估和逐出。                                                                                                                                                                                                                                                                                                                                           运算符在他们从输入流接收到所有快照barriers时,在向其输出流发出barriers之前立即对其状态进行快照。此时,将根据barriers之前的记录对状态进行所有更新,并且在应用barriers之后不依赖于记录的更新。由于快照的状态可能很大,因此它存储在可配置的状态后端(state backend)中。默认情况下,这是JobManager的内存,但对于生产使用,应配置分布式可靠存储(例如HDFS)。在存储状态之后,运算符确认检查点,将快照barriers发送到输出流中,然后继续。
    • 生成的快照现在包含:
      • 对于每个并行流数据源,启动快照时流中的偏移/位置。
      • 对于每个运算符,指向作为快照的一部分存储的状态的指针。 
  • 仅有一次或至少一次:对齐(alignment)步骤可以增加流式传输程序的等待时间。Flink可以在检查点期间跳过流对齐。一旦运算符看到每个输入的检查点barrier,仍然会绘制检查点快照。当跳过对齐时,即使在检查点n的某些检查点barrier到达之后,运算符仍继续处理所有输入这样,操作员还可以在获取检查点n的状态快照之前处理属于检查点n + 1的元素在还原时,这些记录将作为重复记录出现,因为它们都包含在检查点n的状态快照中,并将在检查点n之后作为数据的一部分进行重放对齐仅适用于具有多个前驱(连接)的运算符以及具有多个发送方的运算符(在流重新分区/随机播放之后)。正因为如此,即使在至少一次(at least once)模式中,数据流实际上在尴尬的并行流操作(map()flatMap()filter(),...)中给了正好一次(exactly once)保证
  • 异步状态快照:上述机制意味着运算符在将状态的快照存储在状态后端时停止处理输入记录每次拍摄快照时,同步状态快照都会引入延迟。可以让运算符在存储状态快照时继续处理,有效地让状态快照后台异步发生为此,运算符必须能够生成一个状态对象,该状态对象应以某种方式存储,以便对运算符状态的进一步修改不会影响该状态对象。                                                   在接收到输入的检查点barriers后,运算符启动其状态的异步快照复制。它立即释放其输出的barriers,并继续进行常规流处理。后台复制过程完成后,它会向检查点协调者(JobManager)确认检查点。检查点现在仅在所有接收器都已收到barriers并且所有有状态运算符已确认其完成备份(可能在barriers到达接收器之后)之后才完成。
  • 恢复:当失败时,Flink选择最新完成的检查点k然后,系统重新部署整个分布式数据流,并为每个操作符提供作为检查点k的一部分的快照的状态。设置源从位置Sk开始读取流。例如,在Apache Kafka中,这意味着告诉消费者从偏移量Sk开始提取。如果状态以递增方式快照,则运算符从最新完整快照的状态开始,然后对该状态应用一系列增量快照进行更新。
  • 运算符快照实现:在执行运算符快照时,有同步和异步两部分。运算符和状态后端将他们的快照作为一个Java FutureTask。该任务包含已完成的同步部分且处于挂起状态的异步部分。然后异步部分由该检查点的后台线程执行。检查点纯粹同步地返回已经完成的运算符FutureTask,如果需要执行异步操作,则以该run()方法执行FutureTask任务是可取消的,因此流和其他消耗句柄的资源是可以被释放的。
posted @ 2018-08-15 17:42  gqb00  阅读(1364)  评论(1编辑  收藏  举报