flink 实现端到端的exactly-once语义

文章目录

  • 1. 三种语义介绍
  • 2. Exactly-Once实现的三个地方
  • 3. 具体实现过程
    • 3.1 source阶段
    • 3.2 operator阶段
    • 3.3 sink阶段
  • 4. 两段式提交的具体实现

1. 三种语义介绍

通常情况下,流式计算系统都会为用户提供数据处理的可靠模式功能,用来表明在实际生产运行中会对数据处理做哪些保障。一般来说,流处理引擎通常为用户的应用程序提供三种数据处理语义:最多一次,至少一次和精确一次。

  • 最多一次(At-most-Once):这种语义理解起来很简单,用户的数据只会被处理一次,不管成功还是失败,不会重试也不会重发。
  • 至少一次(At-least-Once):这种语义下,系统会保证数据或事件至少被处理一次。如果发生错误或者丢失,那么会从源头重新发送一条然后进入处理系统。所以同一个事件或者消息会被处理很多次。
  • 精确一次(Exactly-Once):表示每一条数据只会被精确地处理一次,不多也不少。

Exactly-Once是Flink,Spark等流处理系统的核心特性之一,这种语义会保证每一条消息只被流处理系统处理一次。”精确一次“语义是Flink 1.4.0版本引入的一个重要特性,而且,Flink号称支持”端到端的精确一次“语义。

这里解释一下”端到端的精确一次“,它指的是Flink应用从Source端开始到Sink端结束,数据必须经过的起始点和结束点。Flink自身是无法保证外部系统”精确一次“语义的,所以Flink若要实现所谓”端到端的精确一次“的要求,那么外部系统必须支持”精确一次“语义,然后借助Flink提供的checkpoint和两阶段提交才能实现。

来自:https://segmentfault.com/a/1190000022891333

2. Exactly-Once实现的三个地方

要真正实现流数据处理的Exactly-Once语义,一共要保证三个地方。

1)外部应用数据输入到source要保证Exactly-Once语义。
2)flink内部的数据处理要保证Exactly-Once语义。
3)sink将数据输入到外部应用要保证Exactly-Once语义。

那么flink对这三个阶段是如何保证的呢?这里以kafka-flink-kafka为例简单概述:

  • 内部 —— 利用checkpoint机制,把状态存盘,发生故障的时候可以恢复,保证内部的状态一致性
  • source —— kafka consumer作为source,可以将偏移量保存下来,如果后续任务出现了故障,恢复的时候可以由连接器重置偏移量,重新消费数据,保证一致性
  • sink —— kafka producer作为sink,采用两阶段提交 sink,需要实现一个 TwoPhaseCommitSinkFunction

3. 具体实现过程

如果对checkpoint不太了解的同学可以看这篇文章:
https://blog.csdn.net/weixin_44844089/article/details/117192040

3.1 source阶段

source算子会向数据流中添加barrier,对于source任务而言,就会把当前的offset作为状态保存起来。下次从checkpoint恢复时,source任务可以重新提交偏移量,从上次保存的位置开始重新消费数据。

3.2 operator阶段

这个就是flink内部的checkpoint机制就可以完成

3.3 sink阶段

sink阶段实际上就比较麻烦,需要使用两段式提交的过程来保证Exactly-Once语义。

sink 任务首先把数据写入外部 kafka,这些数据都属于预提交的事务(还不能被消费);当遇到 barrier 时,把状态保存到状态后端,并开启新的预提交事务。

flink 实现端到端的exactly-once语义_第1张图片
当所有算子任务的快照完成,也就是这次的 checkpoint 完成时,JobManager 会向所有任务发通知,确认这次 checkpoint 完成。

当sink 任务收到确认通知,就会正式提交之前的事务,kafka 中未确认的数据就改为“已确认”,数据就真正可以被消费了。

flink 实现端到端的exactly-once语义_第2张图片
所以我们看到,执行过程实际上是一个两段式提交,每个算子执行完成,会进行“预提交”,直到执行完sink操作,会发起“确认提交”,如果执行失败,预提交会放弃掉。

具体的两阶段提交步骤总结如下:

  • 第一条数据来了之后,开启一个 kafka 的事务(transaction),正常写入 kafka 分区日志但标记为未提交,这就是“预提交”
  • jobmanager 触发 checkpoint 操作,barrier 从 source 开始向下传递,遇到 barrier 的算子将状态存入状态后端,并通知 jobmanager
  • sink 连接器收到 barrier,保存当前状态,存入 checkpoint,通知jobmanager,并开启下一阶段的事务,用于提交下个检查点的数据
  • jobmanager 收到所有任务的通知,发出确认信息,表示 checkpoint 完成
  • sink 任务收到 jobmanager 的确认信息,正式提交这段时间的数据
  • 外部kafka关闭事务,提交的数据可以正常消费了。

4. 两段式提交的具体实现

需要实现TwoPhaseCommitSinkFunction,里面有四个核心抽象方法:

1、beginTransaction 这是是开始一个事务

2、preCommit 预提交操作

3、commit 提交操作

4、abort 终止 放弃一个事务

具体怎么实现我就不会了

你可能感兴趣的:(Flink面试)