59-flink-状态管理和一致性-容错:
Flink状态管理
Flink中的状态 :https://blog.csdn.net/dongkang123456/article/details/108430338
Flink状态管理详解:Keyed State和Operator List State深度解析 : https://zhuanlan.zhihu.com/p/104171679
- 算子状态(Operator State)
- 键控状态(Keyed State)
- 状态后端(State Backends)
1 状态概述
- 由一个任务维护,并且用来计算某个结果的所有数据,都属于这个任务的状态
- 可以认为任务状态就是一个本地变量,可以被任务的业务逻辑访问
- Flink 会进行状态管理,包括状态一致性、故障处理以及高效存储和访问,以便于开发人员可以专注于应用程序的逻辑
- 在Flink中,状态始终与特定算子相关联
- 为了使运行时的Flink了解算子的状态,算子需要预先注册其状态
总的来说,有两种类型的状态:
- 算子状态(Operator State)
- 算子状态的作用范围限定为算子任务(也就是不能跨任务访问)
- 键控状态(Keyed State)
- 根据输入数据流中定义的键(key)来维护和访问
2 算子状态 Operator State
- 算子状态的作用范围限定为算子任务,同一并行任务所处理的所有数据都可以访问到相同的状态。
- 状态对于同一任务而言是共享的。(不能跨slot)
- 状态算子不能由相同或不同算子的另一个任务访问。
算子状态数据结构
- 列表状态(List state)
- 将状态表示为一组数据的列表
- 联合列表状态(Union list state)
- 也将状态表示未数据的列表。它与常规列表状态的区别在于,在发生故障时,或者从保存点(savepoint)启动应用程序时如何恢复
- 广播状态(Broadcast state)
- 如果一个算子有多项任务,而它的每项任务状态又都相同,那么这种特殊情况最适合应用广播状态
测试代码
src/main/java/com/zh/flink/s02_flink_api/s05_state/StateTest1_OperatorState.java
输入(本地开启socket后输入)
sensor_1,1547718199,35.8
sensor_1,1547718199,35.8
sensor_1,1547718199,35.8
sensor_1,1547718199,35.8
sensor_1,1547718199,35.8
输出
1
2
3
4
5
3 键控状态 Keyed State
- 键控状态是根据输入数据流中定义的键(key)来维护和访问的。
- Flink 为每个key维护一个状态实例,并将具有相同键的所有数据,都分区到同一个算子任务中,这个任务会维护和处理这个key对应的状态。
- 当任务处理一条数据时,他会自动将状态的访问范围限定为当前数据的key。
3.1、键控状态数据结构
- 值状态(value state):将状态表示为单个的值
- 列表状态(List state):将状态表示为一组数据的列表
- 映射状态(Map state):将状态表示为一组key-value对
- 聚合状态(Reducing state & Aggregating State):将状态表示为一个用于聚合操作的列表
3.2、测试代码
src/main/java/com/zh/flink/s02_flink_api/s05_state/StateTest2_KeyedState.java
*注:声明一个键控状态,一般在算子的open()中声明,因为运行时才能获取上下文信息
场景测试
假设做一个温度报警,如果一个传感器前后温差超过10度就报警。这里使用键控状态Keyed State + flatMap来实现
-
java代码
src/main/java/com/zh/flink/s02_flink_api/s05_state/StateTest3_KeyedStateApplicationCase.java
-
启动socket
nc -lk 7777
-
输入数据,查看结果
-
输入
sensor_1,1547718199,35.8 sensor_1,1547718199,32.4 sensor_1,1547718199,42.4 sensor_10,1547718205,52.6 sensor_10,1547718205,22.5 sensor_7,1547718202,6.7 sensor_7,1547718202,9.9 sensor_1,1547718207,36.3 sensor_7,1547718202,19.9 sensor_7,1547718202,30
-
输出
中间没有输出(sensor_7,9.9,19.9),应该是double浮点数计算精度问题,不管它
(sensor_1,32.4,42.4) (sensor_10,52.6,22.5) (sensor_7,19.9,30.0)
-
4、状态后端 State Backends
4.1 概述
- 每传入一条数据,有状态的算子任务都会读取和更新状态。
- 由于有效的状态访问对于处理数据的低延迟至关重要,因此每个并行任务都会在本地维护其状态,以确保快速的状态访问。
- 状态的存储、访问以及维护,由一个可插入的组件决定,这个组件就叫做状态后端( state backend)
- 状态后端主要负责两件事:本地状态管理,以及将检查点(checkPoint)状态写入远程存储
4.2 选择一个状态后端
- MemoryStateBackend
- 内存级的状态后端,会将键控状态作为内存中的对象进行管理,将它们存储在TaskManager的JVM堆上,而将checkpoint存储在JobManager的内存中
- 特点:快速、低延迟,但不稳定
- FsStateBackend(默认)
- 将checkpoint存到远程的持久化文件系统(FileSystem)上,而对于本地状态,跟MemoryStateBackend一样,也会存在TaskManager的JVM堆上
- 同时拥有内存级的本地访问速度,和更好的容错保证
- RocksDBStateBackend
- 将所有状态序列化后,存入本地的RocksDB中存储
4.3 配置文件
flink-conf.yaml
#==============================================================================
# Fault tolerance and checkpointing
#==============================================================================
# The backend that will be used to store operator state checkpoints if
# checkpointing is enabled.
#
# Supported backends are 'jobmanager', 'filesystem', 'rocksdb', or the
# <class-name-of-factory>.
#
# state.backend: filesystem
上面这个就是默认的checkpoint存在filesystem
# Directory for checkpoints filesystem, when using any of the default bundled
# state backends.
#
# state.checkpoints.dir: hdfs://namenode-host:port/flink-checkpoints
# Default target directory for savepoints, optional.
#
# state.savepoints.dir: hdfs://namenode-host:port/flink-savepoints
# Flag to enable/disable incremental checkpoints for backends that
# support incremental checkpoints (like the RocksDB state backend).
#
# state.backend.incremental: false
# The failover strategy, i.e., how the job computation recovers from task failures.
# Only restart tasks that may have been affected by the task failure, which typically includes
# downstream tasks and potentially upstream tasks if their produced data is no longer available for consumption.
jobmanager.execution.failover-strategy: region
上面这个region指,多个并行度的任务要是有个挂掉了,只重启那个任务所属的region(可能含有多个子任务),而不需要重启整个Flink程序
4.4 样例代码
-
其中使用RocksDBStateBackend需要另外加入pom依赖
<!-- RocksDBStateBackend --> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-statebackend-rocksdb_${scala.binary.version}</artifactId> <version>${flink.version}</version> </dependency>
-
java代码
src/main/java/com/zh/flink/s02_flink_api/s05_state/StateTest4_FaultTolerance.java
ProcessFunction API(底层API)
学习的转换算子是无法访问事件的时间戳信息和水位线信息的。而这在一些应用场景下,极为重要。例如MapFunction这样的map转换算子就无法访问时间戳或者当前事件的事件时间。
DataStream API提供了一系列的Low-Level转换算子。可以访问时间戳、watermark以及注册定时事件。还可以输出特定的一些事件,例如超时事件等。Process Function用来构建事件驱动的应用以及实现自定义的业务逻辑(使用之前的window函数和转换算子无法实现)。例如,FlinkSQL就是使用Process Function实现的。
Flink提供了8个Process Function:
- ProcessFunction
- KeyedProcessFunction
- CoProcessFunction
- ProcessJoinFunction
- BroadcastProcessFunction
- KeyedBroadcastProcessFunction
- ProcessWindowFunction
- ProcessAllWindowFunction
1、KeyedProcessFunction
是相对比较常用的ProcessFunction,根据名字就可以知道是用在keyedStream上的。
KeyedProcessFunction用来操作KeyedStream。KeyedProcessFunction会处理流的每一个元素,输出为0个、1个或者多个元素。所有的Process Function都继承自RichFunction接口,所以都有open()
、close()
和getRuntimeContext()
等方法。而KeyedProcessFunction<K, I, O>
还额外提供了两个方法:
processElement(I value, Context ctx, Collector<O> out)
,流中的每一个元素都会调用这个方法,调用结果将会放在Collector数据类型中输出。Context可以访问元素的时间戳,元素的 key ,以及TimerService 时间服务。 Context 还可以将结果输出到别的流(side outputs)。onTimer(long timestamp, OnTimerContext ctx, Collector<O> out)
,是一个回调函数。当之前注册的定时器触发时调用。参数timestamp 为定时器所设定的触发的时间戳。Collector 为输出结果的集合。OnTimerContext和processElement的Context 参数一样,提供了上下文的一些信息,例如定时器触发的时间信息(事件时间或者处理时间)。
测试代码
设置一个获取数据后第5s给出提示信息的定时器。
src/main/java/com/zh/flink/s02_flink_api/s06_processfunction/ProcessTest1_KeyedProcessFunction.java
启动本地socket
nc -lk 7777
输入
sensor_1,1547718207,36.3
输出
8
1612283803911 定时器触发
2、TimerService和定时器(Timers)
Context 和OnTimerContext 所持有的TimerService 对象拥有以下方法:
long currentProcessingTime()
返回当前处理时间long currentWatermark()
返回当前watermark 的时间戳void registerProcessingTimeTimer( long timestamp)
会注册当前key的processing time的定时器。当processing time 到达定时时间时,触发timer。void registerEventTimeTimer(long timestamp)
会注册当前key 的event time 定时器。当Watermark水位线大于等于定时器注册的时间时,触发定时器执行回调函数。void deleteProcessingTimeTimer(long timestamp)
删除之前注册处理时间定时器。如果没有这个时间戳的定时器,则不执行。void deleteEventTimeTimer(long timestamp)
删除之前注册的事件时间定时器,如果没有此时间戳的定时器,则不执行。
当定时器timer 触发时,会执行回调函数onTimer()。注意定时器timer 只能在keyed streams 上面使用。
测试代码
下面举个例子说明KeyedProcessFunction 如何操作KeyedStream。
需求:监控温度传感器的温度值,如果温度值在10 秒钟之内(processing time)连续上升,则报警。
-
java代码
src/main/java/com/zh/flink/s02_flink_api/s06_processfunction/ProcessTest2_ApplicationCase.java
-
启动本地socket,之后输入数据
nc -lk 7777
-
输入
sensor_1,1547718199,35.8 sensor_1,1547718199,34.1 sensor_1,1547718199,34.2 sensor_1,1547718199,35.1 sensor_6,1547718201,15.4 sensor_7,1547718202,6.7 sensor_10,1547718205,38.1 sensor_10,1547718205,39 sensor_6,1547718201,18 sensor_7,1547718202,9.1
-
输出
传感器sensor_1温度值连续10000ms上升 传感器sensor_10温度值连续10000ms上升 传感器sensor_6温度值连续10000ms上升 传感器sensor_7温度值连续10000ms上升
-
3、侧输出流(SideOutput)
- 一个数据可以被多个window包含,只有其不被任何window包含的时候(包含该数据的所有window都关闭之后),才会被丢到侧输出流。
- 简言之,如果一个数据被丢到侧输出流,那么所有包含该数据的window都由于已经超过了"允许的迟到时间"而关闭了,进而新来的迟到数据只能被丢到侧输出流!
- 大部分的DataStream API 的算子的输出是单一输出,也就是某种数据类型的流。除了split 算子,可以将一条流分成多条流,这些流的数据类型也都相同。
- processfunction 的side outputs 功能可以产生多条流,并且这些流的数据类型可以不一样。
- 一个side output 可以定义为OutputTag[X]对象,X 是输出流的数据类型。
- processfunction 可以通过Context 对象发射一个事件到一个或者多个side outputs。
测试代码
场景:温度>=30放入高温流输出,反之放入低温流输出
-
java代码
src/main/java/com/zh/flink/s02_flink_api/s06_processfunction/ProcessTest3_SideOuptCase.java
-
本地启动socket
-
输入
sensor_1,1547718199,35.8 sensor_6,1547718201,15.4 sensor_7,1547718202,6.7 sensor_10,1547718205,38.1
-
输出
high-temp> SensorReading{id='sensor_1', timestamp=1547718199, temperature=35.8} low-temp> SensorReading{id='sensor_6', timestamp=1547718201, temperature=15.4} low-temp> SensorReading{id='sensor_7', timestamp=1547718202, temperature=6.7} high-temp> SensorReading{id='sensor_10', timestamp=1547718205, temperature=38.1}
-
4、CoProcessFunction
- 对于两条输入流,DataStream API 提供了CoProcessFunction 这样的low-level操作。CoProcessFunction 提供了操作每一个输入流的方法:
processElement1()
和processElement2()
。 - 类似于ProcessFunction,这两种方法都通过Context 对象来调用。这个Context对象可以访问事件数据,定时器时间戳,TimerService,以及side outputs。
- CoProcessFunction 也提供了onTimer()回调函数。
容错机制
Flink-容错机制 | 一致性检查点 | 检查点到恢复状态过程 | Flink检查点算法(Chandy-Lamport) | 算法操作解析 | 保存点简介:https://blog.csdn.net/qq_40180229/article/details/106433621
1 一致性检查点(checkpoint)
-
Flink 故障恢复机制的核心,就是应用状态的一致性检查点
-
有状态流应用的一致检查点,其实就是所有任务的状态,在某个时间点的一份拷贝(一份快照);这个时间点,应该是所有任务都恰好处理完一个相同的输入数据的时候
(5这个数据虽然进了奇数流但是偶数流也应该做快照,因为属于同一个相同数据,只是没有被他处理)(这里根据奇偶性分流,偶数流求偶数和,奇数流求奇数和,5这里明显已经被sum_odd(1+3+5)处理了,且sum_even不需要处理该数据,因为前面已经判断该数据不需要到sum_even流,相当于所有任务都已经处理完source的数据5了。)
-
在JobManager中也有个Chechpoint的指针,指向了仓库的状态快照的一个拓扑图,为以后的数据故障恢复做准备
2 从检查点恢复状态
-
在执行流应用程序期间,Flink 会定期保存状态的一致检查点
-
如果发生故障, Flink 将会使用最近的检查点来一致恢复应用程序的状态,并重新启动处理流程
(如图中所示,7这个数据被source读到了,准备传给奇数流时,奇数流宕机了,数据传输发生中断)
-
遇到故障之后,第一步就是重启应用
(重启后,起初流都是空的)
-
第二步是从 checkpoint 中读取状态,将状态重置
(读取在远程仓库(Storage,这里的仓库指状态后端保存数据指定的三种方式之一)保存的状态)
-
从检查点重新启动应用程序后,其内部状态与检查点完成时的状态完全相同
-
第三步:开始消费并处理检查点到发生故障之间的所有数据
-
这种检查点的保存和恢复机制可以为应用程序状态提供“精确一次”(exactly-once)的一致性,因为所有算子都会保存检查点并恢复其所有状态,这样一来所有的输入流就都会被重置到检查点完成时的位置
(这里要求source源也能记录状态,回退到读取数据7的状态,kafka有相应的偏移指针能完成该操作)
3 Flink检查点算法
checkpoint和Watermark一样,都会以广播的形式告诉所有下游。
-
一种简单的想法
暂停应用,保存状态到检查点,再重新恢复应用(Flink不是采用这种简单粗暴的方式)
-
Flink的改进实现
- 基于Chandy-Lamport算法的分布式快照
- 将检查点的保存和数据处理分离开,不暂停整个应用
(就是每个任务单独拍摄自己的快照到内存,之后再到JobManager整合)
- 检查点分界线(Checkpoint Barrier)
- Flink的检查点算法用到了一种称为分界线(barrier)的特殊数据形式,用来把一条流上数据按照不同的检查点分开
- 分界线之前到来的数据导致的状态更改,都会被包含在当前分界线所属的检查点中;而基于分界线之后的数据导致的所有更改,就会被包含在之后的检查点中
具体讲解
- 现在是一个有两个输入流的应用程序,用并行的两个 Source 任务来读取
- 两条自然数数据流,蓝色数据流已经输出完
蓝3
了,黄色数据流输出完黄4
了 - 在Souce端 Source1 接收到了数据
蓝3
正在往下游发向一个数据蓝2 和 蓝3
; Source2 接受到了数据黄4
,且往下游发送数据黄4
- 偶数流已经处理完
黄2
所以后面显示为2, 奇数流处理完蓝1 和 黄1 黄3
所以为5,并分别往下游发送每次聚合后的结果给Sink
-
JobManager 会向每个 source 任务发送一条带有新检查点 ID 的消息,通过这种方式来启动检查点
(这个带有新检查点ID的东西为barrier,由图中三角型表示,数值2只是ID)
- 数据源将它们的状态写入检查点,并发出一个检查点barrier
- 状态后端在状态存入检查点之后,会返回通知给source任务,source任务就会向JobManager确认检查点完成
上图,在Source端接受到barrier后,将自己此身的3 和 4 的数据的状态写入检查点,且向JobManager发送checkpoint成功的消息,然后向下游分别发出一个检查点 barrier
可以看出在Source接受barrier时,数据流也在不断的处理,不会进行中断
此时的偶数流已经处理完蓝2
变成了4,但是还没处理到黄4
,只是下游sink发送了一个数据4,而奇数流已经处理完蓝3
变成了8(黄1+蓝1+黄3+蓝3),并向下游sink发送了8
此时检查点barrier都还未到Sum_odd奇数流和Sum_even偶数流
- 分界线对齐:barrier向下游传递,sum任务会等待所有输入分区的barrier到达
- 对于barrier已经达到的分区,继续到达的数据会被缓存
- 而barrier尚未到达的分区,数据会被正常处理
此时蓝色流的barrier先一步抵达了偶数流,黄色的barrier还未到,但是因为数据的不中断一直处理,此时的先到的蓝色的barrier会将此时的偶数流的数据4进行缓存处理,流接着处理接下来的数据等待着黄色的barrier的到来,而黄色barrier之前的数据将会对缓存的数据相加
这次处理的总结:分界线对齐:barrier 向下游传递,sum 任务会等待所有输入分区的 barrier 到达,对于barrier已经到达的分区,继续到达的数据会被缓存。而barrier尚未到达的分区,数据会被正常处理
- 当收到所有输入分区的 barrier 时,任务就将其状态保存到状态后端的检查点中,然后将 barrier 继续向下游转发
当蓝色的barrier和黄色的barrier(所有分区的)都到达后,进行状态保存到远程仓库,然后对JobManager发送消息,说自己的检查点保存完毕了
*此时的偶数流和奇数流都为8
-
向下游转发检查点 barrier 后,任务继续正常的数据处理
-
Sink 任务向 JobManager 确认状态保存到 checkpoint 完毕
-
当所有任务都确认已成功将状态保存到检查点时,检查点就真正完成了
4 保存点(Savepoints)
CheckPoint为自动保存,SavePoint为手动保存
- Flink还提供了可以自定义的镜像保存功能,就是保存点(save points)
- 原则上,创建保存点使用的算法与检查点完全相同,因此保存点可以认为就是具有一些额外元数据的检查点
- Flink不会自动创建保存点,因此用户(或者外部调度程序)必须明确地触发创建操作
- 保存点是一个强大的功能。除了故障恢复外,保存点可以用于:有计划的手动备份、更新应用程序、版本迁移、暂停和重启程序,等等
5 检查点和重启策略配置
-
java样例代码
src/main/java/com/zh/flink/s02_flink_api/s05_state/StateTest4_FaultTolerance.java
6 状态一致性
Flink-状态一致性 | 状态一致性分类 | 端到端状态一致性 | 幂等写入 | 事务写入 | WAL | 2PC:https://blog.csdn.net/qq_40180229/article/details/106445029
6.1 概述
- 有状态的流处理,内部每个算子任务都可以有自己的状态
- 对于流处理器内部来说,所谓的状态一致性,其实就是我们所说的计算结果要保证准确。
- 一条数据不应该丢失,也不应该重复计算
- 在遇到故障时可以恢复状态,恢复以后的重新计算,结果应该也是完全正确的。
6.2 分类
Flink的一个重大价值在于,它既保证了exactly-once,也具有低延迟和高吞吐的处理能力。
-
AT-MOST-ONCE(最多一次) 当任务故障时,最简单的做法是什么都不干,既不恢复丢失的状态,也不重播丢失的数据。At-most-once 语义的含义是最多处理一次事件。
这其实是没有正确性保障的委婉说法——故障发生之后,计算结果可能丢失。类似的比如网络协议的udp。
-
AT-LEAST-ONCE(至少一次) 在大多数的真实应用场景,我们希望不丢失事件。这种类型的保障称为 at-least-once,意思是所有的事件都得到了处理,而一些事件还可能被处理多次。
这表示计数结果可能大于正确值,但绝不会小于正确值。也就是说,计数程序在发生故障后可能多算,但是绝不会少算。
-
EXACTLY-ONCE(精确一次) 恰好处理一次是最严格的保证,也是最难实现的。恰好处理一次语义不仅仅意味着没有事件丢失,还意味着针对每一个数据,内部状态仅仅更新一次。
这指的是系统保证在发生故障后得到的计数结果与正确值一致。
6.3 一致性检查点(Checkpoints)
- Flink使用了一种轻量级快照机制——检查点(checkpoint)来保证exactly-once语义
- 有状态流应用的一致检查点,其实就是:所有任务的状态,在某个时间点的一份备份(一份快照)。而这个时间点,应该是所有任务都恰好处理完一个相同的输入数据的时间。
- 应用状态的一致检查点,是Flink故障恢复机制的核心
端到端(end-to-end)状态一致性
- 目前我们看到的一致性保证都是由流处理器实现的,也就是说都是在Flink流处理器内部保证的;而在真实应用中,流处理应用除了流处理器以外还包含了数据源(例如Kafka)和输出到持久化系统
- 端到端的一致性保证,意味着结果的正确性贯穿了整个流处理应用的始终;每一个组件都保证了它自己的一致性
- 整个端到端的一致性级别取决于所有组件中一致性最弱的组件
端到端 exactly-once
- 内部保证——checkpoint
- source端——可重设数据的读取位置
- sink端——从故障恢复时,数据不会重复写入外部系统
- 幂等写入
- 事务写入
幂等写入
-
所谓幂等操作,是说一个操作,可以重复执行很多次,但只导致一次结果更改,也就是说,后面再重复执行就不起作用了。
(中间可能会存在不正确的情况,只能保证最后结果正确。比如5=>10=>15=>5=>10=>15,虽然最后是恢复到了15,但是中间有个恢复的过程,如果这个过程能够被读取,就会出问题。)
事务写入
-
事务(Transaction)
- 应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤销
- 具有原子性:一个事务中的一系列的操作要么全部成功,要么一个都不做
-
实现思想
构建的事务对应着checkpoint,等到checkpoint真正完成的时候,才把所有对应的结果写入sink系统中。
-
实现方式
- 预习日志
- 两阶段提交
预写日志(Write-Ahead-Log,WAL)
- 把结果数据先当成状态保存,然后在收到checkpoint完成的通知时,一次性写入sink系统
- 简单易于实现,由于数据提前在状态后端中做了缓存,所以无论什么sink系统,都能用这种方式一批搞定
- DataStream API提供了一个模版类:GenericWriteAheadSink,来实现这种事务性sink
两阶段提交(Two-Phase-Commit,2PC)
- 对于每个checkpoint,sink任务会启动一个事务,并将接下来所有接收到的数据添加到事务里
- 然后将这些数据写入外部sink系统,但不提交它们——这时只是"预提交"
- 这种方式真正实现了exactly-once,它需要一个提供事务支持的外部sink系统。Flink提供了TwoPhaseCommitSinkFunction接口
不同Source和Sink的一致性保证
7 Flink+Kafka 端到端状态一致性的保证
- 内部——利用checkpoint机制,把状态存盘,发生故障的时候可以恢复,保证内部的状态一致性
- source——kafka consumer作为source,可以将偏移量保存下来,如果后续任务出现了故障,恢复的时候可以由连接器重制偏移量,重新消费数据,保证一致性
- sink——kafka producer作为sink,采用两阶段提交sink,需要实现一个TwoPhaseCommitSinkFunction
Exactly-once 两阶段提交
- JobManager 协调各个 TaskManager 进行 checkpoint 存储
- checkpoint保存在 StateBackend中,默认StateBackend是内存级的,也可以改为文件级的进行持久化保存
- 当 checkpoint 启动时,JobManager 会将检查点分界线(barrier)注入数据流
- barrier会在算子间传递下去
- 每个算子会对当前的状态做个快照,保存到状态后端
-
每个内部的 transform 任务遇到 barrier 时,都会把状态存到 checkpoint 里
-
sink 任务首先把数据写入外部 kafka,这些数据都属于预提交的事务;遇到 barrier 时,把状态保存到状态后端,并开启新的预提交事务
(barrier之前的数据还是在之前的事务中没关闭事务,遇到barrier后的数据另外新开启一个事务)
- 当所有算子任务的快照完成,也就是这次的 checkpoint 完成时,JobManager 会向所有任务发通知,确认这次 checkpoint 完成
- sink 任务收到确认通知,正式提交之前的事务,kafka 中未确认数据改为“已确认”
Exactly-once 两阶段提交步骤总结
- 第一条数据来了之后,开启一个 kafka 的事务(transaction),正常写入 kafka 分区日志但标记为未提交,这就是“预提交”
- jobmanager 触发 checkpoint 操作,barrier 从 source 开始向下传递,遇到 barrier 的算子将状态存入状态后端,并通知 jobmanager
- sink 连接器收到 barrier,保存当前状态,存入 checkpoint,通知 jobmanager,并开启下一阶段的事务,用于提交下个检查点的数据
- jobmanager 收到所有任务的通知,发出确认信息,表示 checkpoint 完成
- sink 任务收到 jobmanager 的确认信息,正式提交这段时间的数据
- 外部kafka关闭事务,提交的数据可以正常消费了。