端到端的一致性保证,意味着结果的正确性贯穿了整个流处理应用的始终;每一个组件都保证了它自己的一致性,整个端到端的一致性级别取决于所有组件中一致性最弱的组件。
需要外部源可重设数据的读取位置.
例如使用的Kafka Source具有这种特性: 读取数据的时候可以指定offset
依赖checkpoint机制
需要保证从故障恢复时,数据不会重复写入外部系统. 有2种实现形式:
所谓幂等操作,是说一个操作,可以重复执行很多次,但只导致一次结果更改,也就是说,后面再重复执行就不起作用了。
需要构建事务来写入外部系统,构建的事务对应着 checkpoint,等到 checkpoint 真正完成的时候,才把所有对应的结果写入 sink 系统中。对于事务性写入,具体又有两种实现方式:预写日志(WAL)和两阶段提交(2PC)
1.Flink 使用了 checkPoint 来 保证 exactly-once。Flink集群在某个算子因为某些原因(如 异常退出)出现故障时,能够将整个应用流图的状态恢复到故障之前的某一状态,保证应用流图状态的一致性.
2.Flink是 如何来做这个 checkPoint 的呢?
解释: Flink的checkpoint机制原理来自"Chandy-Lamport algorithm"算法(分布式快照算法)的一种变体: 异步 barrier 快照(asynchronous barrier snapshotting)
这种checkPoint 机制可以再不影响其他算子的情况下,对每一个算子在处理某一时刻 数据的状态进行 checkpoint
步骤:
1>再做checkPoint之前,(jobManager的组件)Checkpoint Coordinator 向所有 source 节点 trigger Checkpoint. source端会 将多个barrier插入到数据流中,然后作为数据流的一部分随着数据流动(有点类似于Watermark).这些barrier不会跨越流中的数据.
2>barrier会把数据流分成两部分: 一部分数据进入当前的快照 , 另一部分数据进入下一个快照, 每个barrier携带着快照的id. barrier 不会暂停数据的流动, 所以非常轻量级. 在流中, 同一时间可以有来源于多个不同快照的多个barrier, 这个意味着可以并发的出现不同的快照(多个算子并发做快照).
source 节点向下游广播 barrier, 下游的task 只有收到所有的input 的barrier 才会执行 Checkpoint
存入 checkpoint,将备份数据的地址通知给 Checkpoint coordinator。这里涉及到 barrier对齐和不对齐。
3>sink 节点收集齐上游两个 input 的 barrier 之后,会执行本地快照,
4>sink 节点在完成自己的 Checkpoint 之后,会将 state handle 返回通知 Coordinator。
5>Checkpoint coordinator 收集齐所有 task 的 state handle,就认为这一次的 Checkpoint 全局完成了,向持久化存储中再备份一个 Checkpoint meta 文件。
如果要实现严格一次, 则要执行barrier对齐。
拥有两个输入流的 Operators(例如 CoProcessFunction)会执行 barrier 对齐(barrier alignment) 以便当前快照能够包含消费两个输入流 barrier 之前(但不超过)的所有 events 而产生的状态。
假设只有两条流(字母流,数字流)
1. 当operator 收到 数字流的 barrier (第n个)时, 他就不能 计算 (能接收) 来自该流的任何数据记录,直到 它从 字母流 收到 同一个 barrier(第n个) 为止。接受到的数据都是 快照 n的 记录。否则,他会包含 快照 n+1 的数据。
2.收到barrier n (数字流的数据 被暂时搁置,这些数据存入缓冲区中,不被处理)
上图红色的 竖线 就代表 barrier
3.当接收到 所有的 barriern 时,Operator 就会把 缓冲区中的 pending 的输出数据 发出去,然后会把 checkPoint barrier n 接着往下游发送 。并对自身进行快照。
依照上述(对齐)第二点 所说的,
假设不对齐, 在字母流的Checkpoint barrier n到达前, 已经处理了1 2 3. 等字母流Checkpoint barrier n到达之后, 会做Checkpoint n. 假设这个时候程序异常错误了, 则重新启动的时候会Checkpoint n之后的数据重新计算. 1 2 3 会被再次被计算, 所以123出现了重复计算.
kafka producer作为sink,采用两阶段提交 sink,需要实现一个 TwoPhaseCommitSinkFunction
1.jobmanager 触发 checkpoint 操作,barrier 从 source 开始向下传递,遇到 barrier 的算子将状态存入状态后端,并通知 jobmanager
2.第一条数据来了之后,开启一个 kafka 的事务(transaction),正常写入 kafka 分区日志但标记为未提交,这就是“预提交”
3.sink 连接器收到 barrier,保存当前状态,存入 checkpoint,通知 jobmanager,并开启下一阶段的事务,用于提交下个检查点的数据
4.jobmanager 收到所有任务的通知,发出确认信息,表示 checkpoint 完成
5.sink 任务收到 jobmanager 的确认信息,正式提交这段时间的数据
6.外部kafka关闭事务,提交的数据可以正常消费了