目的
flink相比其他计算引擎而言有一个重要优点就是可以支持精准一次的,相比于其他计算引擎大多只支持最少一次而言是一个重要特性。这里对flink exactly-once 的使用进行分析,并对其原理进行学习记录。
学习框架图
exactly-once是什么
exactly-once语义指的是每条数据对最终结果的影响只有一次,无论期间硬件或者软件程序出现任何异常使得任务中断,将异常处理恢复flink任务后也不存在重复处理的数据或未处理数据。
exactly-once前提
exactly-once语义的实现是依托于flink集群的checkpoint机制之上。
exactly-once语义实现(两种情况实现其一即可)
1.实现flink的二阶段提交机制
flink在旧版本是仅仅支持了flink内部的精准一次语义。在某个版本的提供了TwoPhaseCommitSinkFunction(二阶段提交输出)后,在resource满足重复拉取 && sink满足事务回滚 && flink开启了检查点的情况下,可以使得整个flink任务也支持exactly-once语义。
2. sink支持幂等性
幂等性:就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次请求而产生了副作用。
在我遇到的sink场景来说:redis是天生幂等性的,mysql里的 insert or update (需要指定唯一索引)操作也是满足幂等性的。
这里因为幂等性的sink在我们通常的数据处理工作中遇到的占比还是比较小的。所以我们主要还是通过flink的二端提交机制来保证exactly-once的语义。接下来主要介绍flink的二阶段提交机制。
二阶段提交机制(Two-phase Commit)
其实二阶段提交机制在分布式系统中并不少见,目的是为了保证命令的原子性。即一个命令要么在所有节点都进行了响应、要么就在所有的节点被拒绝。需要有一个协调节点(master) 收集每个节点对命令的响应,如果所有节点都成功响应就对这个命令予以确认,通知子节点将命令的变动持久化。
flink在1.4版本引入了TwoPhaseCommitSinkFunction的抽象方法来在flink任务生命周期中支持二阶段提交。此前只能保证在flink工作内部保证精准一次(exactly-once)的语义。
TwoPhaseCommitSinkFunction主要需要用户实现以下四个方法来完成flink二阶段提交机制:
-
beginTransaction, 开启一个事务,获得一个句柄
-
preCommit,执行预提交
-
commit ,执行提交
-
abort,放弃一个事务
二阶段提交过程
1. sink支持幂等性
这个比较好理解啦。如redis这类kv类型数据库就十分满足要求。
flink 在运行过程中意外关闭,通过checkpoint恢复到五分钟的计算状态。将五分钟前到现在收集到的数据进行重算,这部分重新计算的结果提交对于sink端的数据没有影响。
2.resource满足重复拉取 && sink满足事务回滚 && flink开启了检查点
首先flink可以依旧自己的策略配置检查点的生成逻辑。在上个检查点和下个检查点之间 所有计算产生的结果数据会进行预提交(自定义preCommit的逻辑)。直到flink触发下一个检查点的生成逻辑并完成了任务中所有算子的快照。 jobmanager会对所有的taskmanager发出任务通知,此时预提交阶段完成。taskmanager执行回调逻辑,jobmanager收集taskmanager的执行结果。若所有节点都完成了数据的提交则生成此次检查点。若存在提交失败的节点,则命令所有节点对预提交的数据进行回滚。