一、端到端的一致性概念
端到端的一致性保证,意味着结果的正确性贯穿了整个流处理应用的始终;每一个组件都保证了它自己的一致性,整个端到端的一致性级别取决于所有组件中一致性最弱的组件。
- source端
需要外部源可重设数据的读取位置.
例如使用的Kafka Source具有这种特性: 读取数据的时候可以指定offset
- flink内部
依赖checkpoint机制
- sink端
需要保证从故障恢复时,数据不会重复写入外部系统. 有2种实现形式:
- 幂等(Idempotent)写入
所谓幂等操作,是说一个操作,可以重复执行很多次,但只导致一次结果更改,也就是说,后面再重复执行就不起作用了。
- 事务性(Transactional)写入
需要构建事务来写入外部系统,构建的事务对应着 checkpoint,等到 checkpoint 真正完成的时候,才把所有对应的结果写入 sink 系统中。对于事务性写入,具体又有两种实现方式:预写日志(WAL)和两阶段提交(2PC)
二、checkpoint 原理
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对齐 和 不对齐
如果要实现严格一次, 则要执行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关闭事务,提交的数据可以正常消费了