Flink exact-once和容错机制

  • Flink checkpoint
  • Checkpoint 性能优化
  • 故障恢复流程
  • Savepoint


对于一个分布式系统来说,单个进程或是节点崩溃导致整个Job失败是经常发生的事情,在异常发生时不会丢失用户数据并能自动恢复才是分布式系统必须支持的特性之一。

Flink checkpoint

Flink基于分布式快照与可部分重发的数据源实现了容错。用户可自定义对整个Job进行快照的时间间隔,当任务失败时,Flink会将整个Job恢复到最近一次快照,并从数据源重发快照之后的数据。
一个简单的Checkpoint的大致流程:

  • 暂停处理新流入数据,将新数据缓存起来。
  • 将算子子任务的本地状态数据拷贝到一个远程的持久化存储上。
  • 继续处理新流入的数据,包括刚才缓存起来的数据。

理解Flink的容错机制,首先需要了解一下Barrier这个概念:

Stream Barrier是Flink分布式Snapshotting中的核心元素,它会作为数据流的记录被同等看待,被插入到数据流中,将数据流中记录的进行分组,并沿着数据流的方向向前推进。每个Barrier会携带一个Snapshot ID,属于该Snapshot的记录会被推向该Barrier的前方。因为Barrier非常轻量,所以并不会中断数据流。带有Barrier的数据流,如下图所示:

flink RichSourceFunction弃用 flink retraction_flink


Flink的Checkpoint逻辑是,一段新数据流入导致状态发生了变化,Flink的算子接收到Checpoint Barrier后,对状态进行快照。每个Checkpoint Barrier有一个ID,表示该段数据属于哪次Checkpoint。如图所示,当ID为n的Checkpoint Barrier到达每个算子后,表示要对n-1和n之间状态的更新做快照。Checkpoint Barrier有点像Event Time中的Watermark,它被插入到数据流中,但并不影响数据流原有的处理顺序。

一个完整的Checkpoint流程示例:

  1. JobManager触发一次Checkpoint(Trigger Checkpoint),这个请求会发送给Source的各个子任务。
  2. flink RichSourceFunction弃用 flink retraction_flink_02

  3. 各Source算子子任务接收到这个Checkpoint请求之后,会将自己的状态写入到状态后端,生成一次快照,并且会向下游所有operator广播Checkpoint Barrier。
    Source算子做完快照后,还会给jobManager发送一个确认,告知自己已经做完了相应的工作。这个确认中包括了一些元数据,其中就包括刚才备份到State Backend的状态句柄,或者说是指向状态的指针。至此,Source完成了一次Checkpoint。
  4. Barrier对齐
    如果下游算子上游又多个输入,这意味着下游算子的多个输入里都有同一个Checkpoint Barrier,而且不同输入里Checkpoint Barrier的流入进度可能不同。Checkpoint Barrier传播的过程需要进行对齐。
    对齐分为四步:
  • 算子子任务在某个输入通道中收到第一个ID为n的Checkpoint Barrier,但是其他输入通道中ID为n的Checkpoint Barrier还未到达,该算子子任务开始准备进行对齐。
  • 算子子任务将第一个输入通道的数据缓存下来,同时继续处理其他输入通道的数据,这个过程被称为对齐。

flink RichSourceFunction弃用 flink retraction_分布式_03

  • 第二个输入通道的Checkpoint Barrier抵达该算子子任务,该算子子任务执行快照,将状态写入State Backend,然后将ID为n的Checkpoint Barrier向下游所有输出通道广播。
  • 对于这个算子子任务,快照执行结束,继续处理各个通道中新流入数据,包括刚才缓存起来的数据。
  1. 数据流图中的每个算子的subtask都经过对齐,快照之后。最后Checkpoint Barrier 到达Sink算子,经过对齐,快照之后,给JobManager发送确认消息。
    4. 当JobManager收到所有算子的Checkpoint确认消息,说明ID为n的Checkpoint执行结束。完成的Checkpoint可以用作Flink Job的故障恢复。

Checkpoint 性能优化

Flink的分布式快照方案可以不中断整个流式应用的处理,但是还是可能增加处理的延迟。

  1. 每次进行Checkpoint前,都需要暂停处理新流入数据,然后开始执行快照,假如状态比较大,一次快照可能长达几秒甚至几分钟。
    针对这个问题,Flink提供了异步快照(Asynchronous Snapshot)的机制。当实际执行快照时,Flink可以立即向下广播Checkpoint Barrier,表示自己已经执行完自己部分的快照。同时,Flink启动一个后台线程,它创建本地状态的一份拷贝,这个线程用来将本地状态的拷贝同步到State Backend上,一旦数据同步完成,再给Checkpoint Coordinator发送确认信息。

  2. Checkpoint Barrier对齐时,必须等待所有上游通道都处理完,假如某个上游通道处理很慢,这可能造成整个数据流堵塞。
    对于要求低延迟,并且能接收at-least-once的应用,Flink允许跳过对齐这一步,或者说一个算子子任务不需要等待所有上游通道的Checkpoint Barrier,直接将Checkpoint Barrier广播,执行快照并继续处理后续流入数据。

故障恢复流程

当应用失败的时候,Flink使用最近的一次Checkpoint,恢复应用的状态,重启应用。
恢复逻辑需要3步:

  1. 重启应用,在集群上重新部署数据流图
  2. 从持久化存储上读取最近一次的Checkpoint数据,加载到各算子子任务上
  3. 继续处理新流入的数据
  4. flink RichSourceFunction弃用 flink retraction_大数据_04


Savepoint

savepoint 本质上就是一次 checkpoint,但它与 checkpoint 的不同在于:

(1)savepoint 需要手动触发

(2)savepoint 不会过期,除非用户明确的处理

savepoint 是用户人为触发的。它依赖于 Flink提供的 client,用户可以通过 client(CLI)来触发一个 savepoint。用户执行触发 savepoint 操作后,client 会给 JobManager 发一个消息,JobManager 接着通知各 TaskManager 触发 checkpoint。checkpoint 触发完成后,TaskManager 会执行 JobManager 的回调, 在回调中JobManager 会告知触发 savepoint 的结果。

flink RichSourceFunction弃用 flink retraction_数据_05


savepoint 不存储 state,它只通过一个指针指向具体的 checkpoint所属的 state。

Savepoint 的使用场景:

(1)应用程序升级

(2)Flink 版本升级

(3)系统升级或系统迁移

(4)程序的模拟仿真情况

(5)A/B 测试