<译>流计算容错
这篇文档描述了Flink的流式计算的容错机制
简介
Flink提供容错机制来对应用数据流提供持续的恢复。这个机制保证了即使在出现错误的情况下,记录也只会被处理一次。注意,这里有一个开关来降级担保至少处理一次(接下来会详细介绍)。
容错机制会持续不断地对分布式数据流画快照。对于一些小状态的流式应用来说,这些快照是很轻量级的,不会非性能造成太大影响。这些流式应用的状态都被保存在一个可配置的地方(如master node或HDFS)
一旦程序出错(可能是机器故障,网络故障,或者一些软件缺陷),Flink会停止这些分布式的数据流。系统会重启它们并重置到最近的一次正常的检查点。输入流会重置到快照点。任何重启后数据流的已经被处理的记录会被确保不会回到之前的检查点状态,也就是说不会再重新去走一遍已经走过的路。
注意:为充分发挥这个机制的保障,数据源(如消息队列或broker)需要支持能够将数据流倒回到一个已定义最近的点,Apache Kafka支持这样,Flink和Kafka连接会充分发挥其能力。
注意:因为Flink的检查点是通过分布式快照来实现的,我们使用字符快照和可交换的检查点
检查点
Flink容错机制最核心的部分就是对分布式流和operater的状态持续不断地画快照。这些快照作为持续不断的检查点来保证一旦程序出现问题后可以回滚。画快照的机制在“Lightweight Asynchronous Snapshots for Distributed Dataflows”里有介绍。它的灵感来自于分布式快照的Chandy-Lamport 算法,并且是转为Flink设计的执行模型
Barriers
Flink分布式快照里的核心元素是brarriers。这些brarris被注入到数据流中成为数据流的一部分。brarris不会超越前面的数据,它们严格线性流动。brarrier 将一个数据流中的记录分离到即将进入目前的一个快照、下一个快照里面去的集合。也就是说brarrier将数据流划分为多个段,每一段去不同的快照。每一个brarrier都带有一个它前面数据即将去的那个快照的ID。Barriers不会中断数据流并且都是非常的轻量级。属于多个快照的多个barriers能够在同一时刻子啊同一个留里面出现,这意味着多个快照可能同步发生。
barriers在数据源端被注入到并行的数据流中。快照n(记为Sn)对应的barriers被注入的位置与该快照所覆盖的数据区域有关。比如说,在Kafka中,这个位置就是在分区中的最后一条数据的偏移。Sn这个位置会传递给检查点的coordinator(Flink job 管理器)。
接下来barriers会继续往下流。当一个中间位置的operator从它所有的输入流中接收到快照n的barrier,它会将这个barrier发送到它输出的所有流里面。当一个sink operator(也就是DAG的终点)从它的所有输入流中接收到barrier n,它会将快照n上报到coordinator。当所有的sink 组件都对快照n进行确认之后,就可以被认为处理完毕了。
当快照n被处理完毕的时候,可以确定的是快照n之前的记录都已经被处理完毕了。因为这些记录(以及它们后面的记录)都已经流经了整个拓扑。
有多个输入流的operater需要根据快照将各个流对齐。以下是对上图的解释:
- 一旦operator从任何一个流里面接收到快照n的barrier,在全部接收其它流中快照n的barrier之前,此operator就不再接收这条流里面的任何记录。然而,这些已经接收的记录可能将快照n的记录和快照n+1的记录混杂了(因为来的时候的数据不能保证按序到达)。
- Streams that report barrier n被暂时搁置。从这些流里面接收到的数据暂时不回被处理,而是先存放到缓冲区。
- 一旦从最后一条输流里面接收到barrier n,opertor发出所有正在等待发出的记录,然后发出快照n的barrier.
- 从这以后,恢复从流里面读取记录,在从流里面读取数据之前,得先读完缓冲区里面的记录。
State
operators可能包含各种形式的State,这些state同样也会成为快照的一部分。operator的state来自以下几种形式:
- 用户自定义state:这是直接由transformation方法(就像 map()或者filter())来创建和修改的一种state。自定义的state可以是一个简单变量,也可以是与function关联的key/value状态(详情参考State in Streaming Applications)
- 系统state:这种状态是用在部分在operator计算缓冲区里面的缓冲数据。一个典型的例子就是窗口缓冲,在这种状态中,系统会不断给窗口收集(或聚合)记录,直到这个窗口被处理成功后发送。.
operators会在收到所有输入流的barrier后及时地对他们的状态进行快照,然后再将barriers发送到输入流。在这个时刻,barriers之前的记录对state的更新都已经被记录,在barriers之后的记录会对state造成的更新都不会被应用。因为一个快照的状态可能是比较大的,它被存储在一个可配置的后端state里。默认情况下,这部分使用的是jobManager的内存,但是在重要的设置步骤中,应该配置一个值得信赖的存储系统(比如HDFS)。state被存储之后,operator会对快照进行确认,将快照的barrier发送到输出流,然后继续处理。
刚生成的快照现在包含:
- 对每一个数据流,在该数据流中该快照的起始位置
- 对每一个opertor,有一个指向作为该快照一部分的state的指针
一次处理语义和至少一次性语义
数据流对齐的步骤可能会对程序增加延时。通常情况下,因为这个导致的延时控制在几毫秒之内,但是在某些存在不正常数据的情况下我们发现延时增加明显。对于应用程序来说通常希望对所有记录保持保持低延迟(几毫秒),Flink有一个开关来跳过流对齐的步骤,检查点的快照机制依然会保持运行。在收到所有流的barrier后对检查点进行快照。
在对齐步骤跳过后,operator保持对各输入流的处理,即便是在只有某些流的barrier到达,依然对这些流继续处理,不搁置(以前会搁置直到所有流对快照 n 的barrier都到达这个operator)。在一次恢复之后,这些记录可能会重复出现,因为它们都被包含在快照n里面,会在快照n之后重现
注意:对齐步骤只应该在需要jion操作以及多个发出流(在流被重新分区或shuffle之后)的情况下才需要启用。因为那些只有一些简单并行流操作(如 map(),flatMap(),filter(),...)的流事实上就已经提供了至少处理一次语义。
恢复
恢复机制是很简单直接的:在一次错误发生的时候,Flink选取最近的一次成功的检查点 k。然后系统重现在这个快照中的整个分布式流,并提供每个operator在此快照中的检查点 k 时的状态。数据源被设置为从Sk位置开始读取。比如在Kafka中,意思就是被告知从偏移为Sk的地方开始消费。
如果快照被不正确地记录,operator会从最近一次正确的快照开始,将接下来的快照增量更新到此state