Flink
在 Flink 中须要端到端精准一次解决的地位有三个:
- Source 端:数据从上一阶段进入到 Flink 时,须要保障音讯精准一次生产。
- Flink 外部端:这个咱们曾经理解,利用 Checkpoint 机制,把状态存盘,产生故障的时候能够复原,保障外部的状态一致性。不理解的小伙伴能够看下我之前的文章:
Flink 可靠性的基石 -checkpoint 机制具体解析
- Sink 端:将解决完的数据发送到下一阶段时,须要保证数据可能准确无误发送到下一阶段。
在 Flink 1.4 版本之前,精准一次解决只限于 Flink 利用内,也就是所有的 Operator 齐全由 Flink 状态保留并治理的能力实现准确一次解决。但 Flink 解决完数据后大多须要将后果发送到内部零碎,比方 Sink 到 Kafka 中,这个过程中 Flink 并不保障精准一次解决。
在 Flink 1.4 版本正式引入了一个里程碑式的性能:两阶段提交 Sink,即 TwoPhaseCommitSinkFunction 函数。该 SinkFunction 提取并封装 了两阶段提交协定 中的公共逻辑,自此 Flink 搭配特定 Source 和 Sink(如 Kafka 0.11 版)实现准确一次解决语义(英文简称:EOS,即 Exactly-Once Semantics)。
端到端精准一次解决语义(EOS)
以下内容实用于 Flink 1.4 及之后版本
对于 Source 端:Source 端的精准一次解决比较简单,毕竟数据是落到 Flink 中,所以 Flink 只须要保留生产数据的偏移量即可,如生产 Kafka 中的数据,Flink 将 Kafka Consumer 作为 Source,能够将偏移量保留下来,如果后续工作呈现了故障,复原的时候能够由连接器重置偏移量,从新生产数据,保障一致性。
对于 Sink 端:Sink 端是最简单的,因为数据是落地到其余零碎上的,数据一旦来到 Flink 之后,Flink 就监控不到这些数据了,所以精准一次解决语义必须也要利用于 Flink 写入数据的内部零碎,故这些内部零碎必须提供一种伎俩容许提交或回滚这些写入操作,同时还要保障与 Flink Checkpoint 可能协调应用(Kafka 0.11 版本曾经实现准确一次解决语义)。
咱们以 Flink 与 Kafka 组合为例,Flink 从 Kafka 中读数据,解决完的数据在写入 Kafka 中。
为什么以 Kafka 为例,第一个起因是目前大多数的 Flink 零碎读写数据都是与 Kafka 零碎进行的。第二个起因,也是 最重要的起因 Kafka 0.11 版本正式公布了对于事务的反对,这是与 Kafka 交互的 Flink 利用要实现端到端精准一次语义的必要条件。
当然,Flink 反对这种精准一次解决语义并不只是限于与 Kafka 的联合,能够应用任何 Source/Sink,只有它们提供了必要的协调机制。
Flink 与 Kafka 组合
如上图所示,Flink 中蕴含以下组件:
- 一个 Source,从 Kafka 中读取数据(即 KafkaConsumer)
- 一个工夫窗口化的团聚操作(Window)
- 一个 Sink,将后果写入到 Kafka(即 KafkaProducer)
若要 Sink 反对精准一次解决语义(EOS),它必须以事务的形式写数据到 Kafka,这样当提交事务时两次 Checkpoint 间的所有写入操作当作为一个事务被提交。这确保了呈现故障或解体时这些写入操作可能被回滚。
当然了,在一个分布式且含有多个并发执行 Sink 的利用中,仅仅执行单次提交或回滚是不够的,因为所有组件都必须对这些提交或回滚达成共识,这样能力保障失去一个一致性的后果。Flink 应用两阶段提交协定以及预提交 (Pre-commit) 阶段来解决这个问题。
两阶段提交协定(2PC)
两阶段提交协定(Two-Phase Commit,2PC)是很罕用的解决分布式事务问题的形式,它能够保障在分布式事务中,要么所有参加过程都提交事务,要么都勾销,即实现 ACID 中的 A(原子性)。
在数据一致性的环境下,其代表的含意是:要么所有备份数据同时更改某个数值,要么都不改,以此来达到数据的 强一致性。
两阶段提交协定中有两个重要角色,协调者(Coordinator)和参与者(Participant),其中协调者只有一个,起到分布式事务的协调治理作用,参与者有多个。
顾名思义,两阶段提交将提交过程划分为间断的两个阶段:表决阶段(Voting)和提交阶段(Commit)。
两阶段提交协定过程如下图所示:
第一阶段:表决阶段
- 协调者向所有参与者发送一个 VOTE_REQUEST 音讯。
- 当参与者接管到 VOTE_REQUEST 音讯,向协调者发送 VOTE_COMMIT 音讯作为回应,通知协调者本人曾经做好筹备提交筹备,如果参与者没有筹备好或遇到其余故障,就返回一个 VOTE_ABORT 音讯,通知协调者目前无奈提交事务。
第二阶段:提交阶段
- 协调者收集来自各个参与者的表决音讯。如果 所有参与者统一认为能够提交事务,那么协调者决定事务的最终提交 ,在此情景下协调者向所有参与者发送一个 GLOBAL_COMMIT 音讯,告诉参与者进行本地提交;如果所有参与者中有 任意一个返回音讯是 VOTE_ABORT,协调者就会勾销事务,向所有参与者播送一条 GLOBAL_ABORT 音讯告诉所有的参与者勾销事务。
- 每个提交了表决信息的参与者等待协调者返回音讯,如果参与者接管到一个 GLOBAL_COMMIT 音讯,那么参与者提交本地事务,否则如果接管到 GLOBAL_ABORT 音讯,则参与者勾销本地事务。
两阶段提交协定在 Flink 中的利用
Flink 的两阶段提交思路:
咱们从 Flink 程序启动到生产 Kafka 数据,最初到 Flink 将数据 Sink 到 Kafka 为止,来剖析 Flink 的精准一次解决。
- 当 Checkpoint 启动时,JobManager 会将检查点分界线(checkpoint battier)注入数据流,checkpoint barrier 会在算子间传递上来,如下如所示:
- Source 端 :Flink Kafka Source 负责保留 Kafka 生产 offset,当 Chckpoint 胜利时 Flink 负责提交这些写入,否则就终止勾销掉它们,当 Chckpoint 实现位移保留,它会将 checkpoint barrier(检查点分界线)传给下一个 Operator,而后每个算子会对以后的状态做个快照, 保留到状态后端(State Backend)。
对于 Source 工作而言,就会把以后的 offset 作为状态保存起来。下次从 Checkpoint 复原时,Source 工作能够从新提交偏移量,从上次保留的地位开始从新生产数据,如下图所示:
- Slink 端 :从 Source 端开始,每个外部的 transform 工作遇到 checkpoint barrier(检查点分界线)时,都会把状态存到 Checkpoint 里。数据处理结束到 Sink 端时,Sink 工作首先把数据写入内部 Kafka,这些数据都属于预提交的事务(还不能被生产), 此时的 Pre-commit 预提交阶段下 Data Sink 在保留状态到状态后端的同时还必须预提交它的内部事务,如下图所示:
- 当所有算子工作的快照实现 (所有创立的快照都被视为是 Checkpoint 的一部分), 也就是这次的 Checkpoint 实现时,JobManager 会向所有工作发告诉,确认这次 Checkpoint 实现,此时 Pre-commit 预提交阶段才算实现 。才正式到 两阶段提交协定的第二个阶段:commit 阶段。该阶段中 JobManager 会为利用中每个 Operator 发动 Checkpoint 已实现的回调逻辑。
本例中的 Data Source 和窗口操作无内部状态,因而在该阶段,这两个 Opeartor 无需执行任何逻辑,然而 Data Sink 是有内部状态的,此时咱们必须提交内部事务,当 Sink 工作收到确认告诉,就会正式提交之前的事务,Kafka 中未确认的数据就改为“已确认”,数据就真正能够被生产了,如下图所示:
注:Flink 由 JobManager 协调各个 TaskManager 进行 Checkpoint 存储,Checkpoint 保留在 StateBackend(状态后端)中,默认 StateBackend 是内存级的,也能够改为文件级的进行长久化保留。
最初,一张图总结下 Flink 的 EOS:
此图倡议保留,总结全面且简明扼要,再也不怂面试官!