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