Flink CheckPoint机制简介

概述 

       Checkpoint是Flink实现容错机制最核心的功能,它能够根据配置周期性地基于Stream中各个Operator/task的状态来生成快照,从而将这些状态数据定期持久化存储下来,当Flink程序一旦意外崩溃时,重新运行程序时可以有选择地从这些快照进行恢复,从而修正因为故障带来的程序数据异常。当然,为了保证exactly-once/at-least-once的特性,还需要数据源支持数据回放。Flink针对不同的容错和消息处理上提供了不同的容错语义,主要分为如下部分:

  1. at most once:至多一次,表示一条消息不管后续处理成功与否只会被消费处理一次,那么就存在数据丢失可能。(该语义在flink内部实现为不开启ckp)
  2. at least once:至少一次,表示一条消息从消费到后续的处理成功,可能会发生多次。(该语义在flink内部实现为开启ckp,并设置为at least once)
  3. exactly once:精确一次,表示一条消息从其消费到后续的处理成功,只会发生一次。(flink引擎内部处理的Exactly-Once语义)、(该语义在flink内部实现为开启ckp,并设置为exactly once)
  4. end to end exactly once:端到端的一致性保证,除了flink应用程序本身的状态,flink写入的外部存储也需要满足这个语义。也就是说,这些外部系统必须提供提交或者回滚的方法,然后通过flink的checkpoint来协调。在flink中,其提供了TwoPhaseCommitSinkFunction的两阶段提交协议来保证端到端的Exactly-Once语义。

       Flink在实现上诉Checkpoint语义上是依赖于带有barrier的分布式快照+可部分重发的数据源功能实现的。在其周期性的分布式快照中,就保存了各个operator算子中的状态信息。Flink的失败恢复依赖于:检查点机制+可部分重发的数据源。

  1. 检查点机制:checkpoint定期触发,产生分布式快照,快照中记录了:
    1. 当前检查点开始时数据源(例如Kafka)中消息的offset
    2. 记录了所有有状态的operator当前的状态信息(例如sum中的数值)
  2. 可部分重发的数据源:Flink选择最近完成的检查点K,然后系统重放整个分布式的数据流,然后给予每个operator他们在检查点K快照中的状态。数据源被设置为从位置offset_k开始重新读取流。例如在Apache Kafka中,那意味着告诉消费者从偏移量offset_k开始重新消费。 

Checkpoint执行机制详解

       Flink分布式快照的核心在于stream barrier,barrier是一种特殊标记的消息,它会作为数据流的一部分和数据一起向下流动。barrier不会干扰正常的数据,数据流严格有序。一个barrier把数据流分割成两部分;一部分进入当前的快照,另一部分进入下一个快照。每一个barrier都带有快照ID,并且barrier之前的数据都进入了此快照。barrier不会干扰数据流处理,所以很轻量。多个不同快照的多个barrier会在流中同时出现。即多个快照可能同时创建。而当一个operator从它所有的input channel中都收到barrier,则会触发当前operator的快照操作,并且向其下游channel中发射barrier。当所有的sink都反馈完成快照之后,Checkpoint coordinator认为检查点创建完毕。

       接下来将对Checkpoint的执行流程逐步拆解进行讲解,其主要触发为JobMaster中的Checkpoint Coordinator,其是整个Checkpoint的发起者,之后便是source算子,经过一系列的transformation算子最终到达sink至外部持久化存储,其主要执行步骤如下:

  1. 第一步,Checkpoint Coordinator向所有source节点trigger Checkpoint;
  2. 第二步,source节点向下游广播barrier,这个barrier就是实现Chandy-Lamport分布式快照算法的核心,下游的task只有收到所有input的barrier才会执行相应的Checkpoint。
  3. 第三步,当task完成state备份后,会将备份数据的地址(state handle)通知给Checkpoint coordinator。
  4. 第四步,下游的transformation节点算子收集齐上游多个input的barrier之后,会异步执行本地的算子状态快照。并将barrier继续向下游传递。
  5. 同样的,sink节点在完成自己的Checkpoint之后,会将state handle返回通知给Coordinator。
  6. 最后,当Checkpoint coordinator收集齐所有task的state handle,就认为这一次的Checkpoint全局完成了,向持久化存储中再备份一个Checkpoint meta文件。

Checkpoint的EXACTLY_ONCE语义

        为了实现EXACTLY ONCE语义,Flink在对多input channel通道输入的算子会进行barrier对齐操作。也就是说在多通道输入的算子中,其在接收到第一个barrier后不会马上做snapshot,而是等待接受其他input channel的barrier。在等待期间,算子会把barrier已到的channel的record放入input buffer缓存起来(仅缓存,不做计算处理),当所有上游channel的barrier到齐后,当前算子operate才进行异步的快照操作,记录当前自身的state状态,并向所有下游channel发送barrier。之后便开始处理input buffer所缓存的数据以及所属下一个barrier的数据流记录。

        而对于AT LEAST ONCE语义,无需进行barrier对齐操作,也就是说在多input channel输入的过程中,无需缓存barrier先到的channel中收集的数据,算子会直接对先到的数据进行处理,所以导致restore时,数据可能会被多次处理。下图是官网文档里面就Checkpoint barrier对齐操作的示意图:

需要特别注意的是,Flink的Checkpoint机制只能保证Flink引擎内的计算过程可以做到EXACTLY ONCE,端到端的EXACTLY ONCE需要source和sink的支持。

Checkpoint的端到端的Exactly_Once语义

        在flink中如果需要实现end to end的Exactly-Once语义,需要依赖于其提供的两阶段提交协议TwoPhaseCommitSinkFunction,其主要用来在data sink端中保证Exactly-Once语义,其会把所属当前ckp-n的所有写入数据通过一个事务提交到外部存储。在两个checkpoint之间,一个外部的事务提交绑定了当前ckp所有需要写入的数据。当前为了保证容错,其写入的数据可以被回滚。其两阶段提交协议TwoPhaseCommitSinkFunction的具体过程如下:

  1. 在checkpoint开始的时候,即两阶段提交中的预提交阶段。在data sink中除了将状态写入到state backend之外,data sink还需将所属该checkpoint的数据通过一次事务操作,向外部存储预提交自己的事务。并向jobmaster中的Checkpoint Coordinator汇报本算子对该ckp操作的响应信息。
  2. 当Checkpoint Coordinator收到了每一个operator算子对该checkpoint操作成功的响应,其会触发checkpoint操作回调,通知所有operator算子该checkpoint已经成功了。这时两阶段提交中的提交阶段,data sink将会把当前预提交的事务进行事务提交,把该checkpoint的输出sink数据持久化存储写入到外部系统。

接下来通过一个简单的文件操作例子来说明如何使用TwoPhaseCommitSinkFunction。只需要实现四个method,并使sink呈现Exactly-Once语义。

  1. beginTransaction - 在事务开始前,在目标文件系统上面的临时目录上创建一个临时文件。随后,在程序处理的时候可以将数据写入到这个文件。
  2. preCommit - 在预提交阶段,刷新文件到磁盘,关闭文件,不要重新打开写入。并且为下一个checkpoint的文件写入开启一个新的文件。
  3. commit - 在提交阶段,原子性的将预提交阶段的文件移动到真正的目标目录。需要注意的是,这增加了输出数据的可见性的延迟。
  4. abort - 在终止阶段,删除临时文件。

Checkpoint的基本配置项

        默认情况下,Checkpoint机制是关闭的,需要调用env.enableCheckpointing(n)来开启,每隔n毫秒进行一次Checkpoint。Checkpoint是一种负载较重的任务,如果状态比较大,同时n值又比较小,那可能一次Checkpoint还没完成,下次Checkpoint已经被触发,占用太多本该用于正常数据处理的资源。增大n值意味着一个作业的Checkpoint次数更少,整个作业用于进行Checkpoint的资源更小,可以将更多的资源用于正常的流数据处理。同时,更大的n值意味着重启后,整个作业需要从更长的Offset开始重新处理数据。

此外,还有一些其他参数需要配置,这些参数统一封装在了CheckpointConfig里:

val cpConfig: CheckpointConfig = env.getCheckpointConfig

默认的Checkpoint配置是支持Exactly-Once设置的,这样能保证在重启恢复时,所有算子的状态对任一条数据只处理一次。用上文的Checkpoint原理来说,使用Exactly-Once就是进行了Checkpoint Barrier对齐,因此会有一定的延迟。如果作业延迟小,那么应该使用At-Least-Once投递,不进行对齐,但某些数据会被处理多次。

// 使用At-Least-Once
env.getCheckpointConfig.setCheckpointingMode(CheckpointingMode.AT_LEAST_ONCE)

如果一次Checkpoint超过一定时间仍未完成,直接将其终止,以免其占用太多资源:

// 超时时间1小时
env.getCheckpointConfig.setCheckpointTimeout(3600*1000)

如果两次Checkpoint之间的间歇时间太短,那么正常的作业可能获取的资源较少,更多的资源被用在了Checkpoint上。对这个参数进行合理配置能保证数据流的正常处理。比如,设置这个参数为60秒,那么前一次Checkpoint结束后60秒内不会启动新的Checkpoint。这种模式只在整个作业最多允许1个Checkpoint时适用。

// 两次Checkpoint的间隔为60秒
env.getCheckpointConfig.setMinPauseBetweenCheckpoints(60*1000)

默认情况下一个作业只允许1个Checkpoint执行,如果某个Checkpoint正在进行,另外一个Checkpoint被启动,新的Checkpoint需要挂起等待。

// 最多同时进行3个Checkpoint
env.getCheckpointConfig.setMaxConcurrentCheckpoints(3)

如果这个参数大于1,将与前面提到的最短间隔相冲突。

Checkpoint的初衷是用来进行故障恢复,如果作业是因为异常而失败,Flink会保存远程存储上的数据;如果开发者自己取消了作业,远程存储上的数据都会被删除。如果开发者希望通过Checkpoint数据进行调试,自己取消了作业,同时希望将远程数据保存下来,需要设置为:

// 作业取消后仍然保存Checkpoint
env.getCheckpointConfig.enableExternalizedCheckpoints(ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION)

RETAIN_ON_CANCELLATION模式下,用户需要自己手动删除远程存储上的Checkpoint数据。

默认情况下,如果Checkpoint过程失败,会导致整个应用重启,我们可以关闭这个功能,这样Checkpoint失败不影响作业的运行。

env.getCheckpointConfig.setFailOnCheckpointingErrors(false)

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值