给人做网站,昆山网站建设工作室,网站主关键词,1688的网站特色前言
一个Flink作业由一系列算子构成#xff0c;每个算子可以有多个并行实例#xff0c;这些实例被称为 subTask#xff0c;每个subTask运行在不同的进程或物理机上#xff0c;以实现作业的并行处理。在这个复杂的分布式场景中#xff0c;任何一个节点故障都有可能导致 F…前言
一个Flink作业由一系列算子构成每个算子可以有多个并行实例这些实例被称为 subTask每个subTask运行在不同的进程或物理机上以实现作业的并行处理。在这个复杂的分布式场景中任何一个节点故障都有可能导致 Flink 作业宕机Flink 状态本地化虽然可以实现极致的访问速度但是节点故障后的状态恢复问题也是Flink必须要解决的。
状态持久化
恢复状态最简单粗暴的方式就是回溯全量数据重新计算一遍。不过缺点也很明显首先有的数据源压根就不支持保存全量数据例如Kafka可能就只保存近几天甚至几小时的数据其次回溯全量数据必然会消耗大量时间导致作业产出结果出现较大延时这本身就和Flink高吞吐低延时的目标相悖。
于是Flink 推出了状态持久化方案Flink 作业运行时会自动、定时地将状态数据持久化到远程分布式文件系统中一旦 Flink 作业异常重启就会从远程分布式文件系统中读取最新的快照恢复状态避免了状态数据丢失的问题。
如下示例数据源会不断产生一些数字Flink 作业会对这些数字求和并输出到目标数据库。第一步数字1输入subTask更新本地状态sum1然后将其持久化到远程文件系统此时作业异常宕机本地状态丢失第二步Flink 作业重启从远程文件系统恢复状态sum1第三步subTask继续处理数据整个过程就像没发生故障一样。 状态一致性
状态持久化只实现了基本的异常容错用户往往还有“状态一致性”的诉求。发生故障时Flink不仅要能从远程文件系统中恢复状态数据还要能协调所有subTask节点在故障恢复后实现数据的精准一次处理也就是数据即不会多算也不会少算以保证作业的计算结果如同没有发生过故障一样。
流计算的状态一致性有三个等级
at-most-once 最多计算一次允许数据丢失最弱的一致性保证at-least-once 至少计算一次允许数据重复计算对于自身具备幂等性写入的业务指标可以保证一致性exactly-once 精准计算一次最强的一致性保证数据不会多算也不会少算
仅仅通过状态持久化只能保证 at-most-once 一致性本地状态更新后还没来得及保存到远程文件系统时发生故障数据就会丢失导致漏算。
如下图所示数据2处理完本地状态更新sum3状态还没来得及持久化就发生故障重启后恢复状态sum1数据2的计算丢失了数据漏算。 要想避免数据漏算可以通过故障恢复时向前回溯一部分数据来解决例如回溯前一小时的数据甚至全部数据这样可以保证数据至少被计算一次也就是满足 at-least-once 一致性但是会有数据被重复计算对于本身具备幂等性的业务指标这没什么问题非幂等性的业务指标计算结果仍不准确。
最理想的一致性场景就是 exactly-once数据精准计算一次既不多算也不少算。在咱们这个例子中要想实现 exactly-once 一致性除了同步sum状态还要同步作业处理数据的偏移量offset故障恢复时根据恢复的offset从指定的位置重新读取数据进行处理。
如图所示第一步处理数字1求和更新本地状态sum1、offset1并持久化到远程文件系统第二步处理数字2求和更新本地状态sum3、offset2状态还没持久化时发生故障本地状态丢失第三步从远程文件系统恢复状态第四步从offset1处开始继续处理数据2更新本地状态并输出结果整个流程就像没发生过故障一样。 由此可见要满足 exactly-once 一致性有以下几个条件
数据源支持根据偏移量回溯subTask持久化状态的同时也要持久化偏移量offsetsubTask持久化状态和处理数据要互斥不能持久化状态的同时还处理数据
一个完整的Flink作业由若干个subTask构成运行在一个复杂的分布式环境中Flink作业状态一致性的前提是每个subTask先保证自身状态一致性。对于Source算子subTask来说如果数据源支持根据offset回溯数据那么执行上述流程不会有问题。但是对于下游非Source算子subTask来说情况会显得更加复杂。
Source算子subTask读取到数据后是通过Socket传输给下游subTask的Socket通道的数据首先不支持回溯其次数据压根就没有offset这就意味着下游subTask可能会漏算数据又回到 at-most-once 一致性了。丢失的这些数据不能让上游subTask重发因为上游subTask根本就不知道下游subTask的处理结果是成功还是失败如果再额外引入一套ACK机制增加复杂度不说额外的性能消耗也是Flink无法承受的。
既然上游不支持重发就只能下游subTask自己解决了。下游subTask在收到上游传过来的数据时除了计算并更新本地状态外还要将收到的这部分数据也写进状态里面打快照时和状态一同持久化。故障恢复时除了恢复状态外再把这部分数据拿出来重新计算一下最终的状态结果就是准确的了。下游subTask也无需保存接收到的所有数据只要数据被计算过且打过快照这部分数据就没用了所以下游subTask要保存的数据只有上游subTask开始执行快照到下游subTask开始执行快照时的这部分数据怎么让下游subTask知道上游subTask在执行快照呢很简单上游subTask执行快照时给下游subTask广播一条特殊的消息即可这个消息被称为“barrier”屏障。
再次总结一下要满足 exactly-once 一致性满足以下条件
数据源支持根据offset回溯Source算子持久化offset并向下游算子广播barriersubTask持久化状态和处理数据要互斥不能持久化状态的同时还处理数据非Source算子subTask要持久化两部分数据本地状态数据、上游subTask执行快照到自己执行快照这段时间接收到的数据所有subTask快照执行成功才算一次完整的快照
故障恢复时Source算子从远程文件系统恢复offset根据offset回溯数据源并发送给下游subTask下游subTask先从远程文件系统恢复状态再读取之前上游发送给自己的数据重新计算一遍这部分数据恢复自身状态再继续处理上游发给自己的数据。
Checkpoint机制
有了上述理论再看Flink Checkpoint机制就很容易理解了。
Flink 以 Chandy-Lamport 算法理论为基础实现了一套分布式轻量级异步快照算法即 Flink Checkpoint。
每个需要Checkpoint的Flink应用启动时JobManager都会为其创建一个 CheckpointCoordinator检查点协调器的组件由它来负责生成全局快照流程如下
CheckpointCoordinator周期性的向所有Source算子的subTask发送barrier开始执行快照Source算子收到barrier暂停处理数据将本地状态持久化到远程文件系统并向CheckpointCoordinator报告自己的快照结果同时向下游subTask广播barrier下游subTask收到barrier同样暂停数据处理。对于有多个输入的subTask来说需要收到所有上游发来的subTask才会开始执行快照这里就存在barrier对齐的问题。subTask同样地将本地状态持久化到远程文件系统并向CheckpointCoordinator报告自己的快照结果同时将barrier转发给下游subTask直到Sink算子当CheckpointCoordinator收到所有算子的快照成功报告之后认为该周期的快照制作成功。如果没有在指定时间内收到所有算子的报告则认定为快照制作失败。
Checkpoint 优化了subTask执行快照的时机避免了整个快照期间所有subTask都要暂停处理数据的问题。CheckpointCoordinator负责通知Source算子执行快照而下游算子执行快照的时机依赖于上游算子发送过来的barrier这套机制执行快照无需暂停整个作业的数据处理有效降低了流处理作业的延时问题。