Flink 和 Kafka 连接时的精确一次保证

在流处理的应用中,最佳的数据源当然就是可重置偏移量的消息队列了;它不仅可以提供数据重放的功能,而且天生就是以流的方式存储和处理数据的。所以作为大数据工具中消息队 列的代表, Kafka 可以说与 Flink 是天作之合, 实际项目中也经常会看到以 Kafka 作为数据源 和写入的外部系统的应用。在本小节中, 我们就来具体讨论一下 Flink 和 Kafka 连接时, 怎样 保证端到端的 exactly-once 状态一致性。

  1. 整体介绍
    既然是端到端的 exactly-once,我们依然可以从三个组件的角度来进行分析:
    (1)Flink 内部
    Flink 内部可以通过检查点机制保证状态和处理结果的 exactly-once 语义。
    (2)输入端
    输入数据源端的 Kafka 可以对数据进行持久化保存,并可以重置偏移量(offset)。所以我 们可以在 Source 任务(FlinkKafkaConsumer)中将当前读取的偏移量保存为算子状态,写入到 检查点中;当发生故障时,从检查点中读取恢复状态,并由连接器 FlinkKafkaConsumer 向 Kafka 重新提交偏移量,就可以重新消费数据、保证结果的一致性了。
    (3)输出端
    输出端保证 exactly-once 的最佳实现, 当然就是两阶段提交(2PC)。作为与 Flink 天生一 对的 Kafka ,自然需要用最强有力的一致性保证来证明自己。
    Flink 官方实现的 Kafka 连接器中,提供了写入到 Kafka 的 FlinkKafkaProducer,它就实现 了 TwoPhaseCommitSinkFunction 接口:
    在这里插入图片描述

也就是说, 我们写入 Kafka 的过程实际上是一个两段式的提交: 处理完毕得到结果, 写入 Kafka 时是基于事务的“预提交”;等到检查点保存完毕, 才会提交事务进行“正式提交”。如 果中间出现故障,事务进行回滚,预提交就会被放弃; 恢复状态之后, 也只能恢复所有已经确 认提交的操作。
2. 具体步骤
为了方便说明, 我们来考虑一个具体的流处理系统,由Flink 从 Kafka 读取数据、并将处 理结果写入 Kafka,如图 10- 14 所示。
在这里插入图片描述
这是一个 Flink 与 Kafka 构建的完整数据管道,Source 任务从 Kafka 读取数据,经过一系 列处理(比如窗口计算),然后由 Sink 任务将结果再写入 Kafka。
Flink 与 Kafka 连接的两阶段提交,离不开检查点的配合, 这个过程需要 JobManager 协调 各个 TaskManager 进行状态快照,而检查点具体存储位置则是由状态后端(State Backend)来 配置管理的。一般情况, 我们会将检查点存储到分布式文件系统上。
实现端到端 exactly-once 的具体过程可以分解如下:
(1)启动检查点保存
检查点保存的启动, 标志着我们进入了两阶段提交协议的“预提交”阶段。当然, 现在还 没有具体提交的数据。
在这里插入图片描述
如图 10- 15 所示,JobManager 通知各个 TaskManager 启动检查点保存,Source 任务会将检 查点分界线(barrier)注入数据流。这个 barrier 可以将数据流中的数据,分为进入当前检查点 的集合和进入下一个检查点的集合。
(2)算子任务对状态做快照
分界线(barrier)会在算子间传递下去。每个算子收到 barrier 时,会将当前的状态做个快 照,保存到状态后端。
在这里插入图片描述
如图 10- 16 所示,Source 任务将 barrier 插入数据流后,也会将当前读取数据的偏移量作 为状态写入检查点, 存入状态后端; 然后把 barrier 向下游传递,自己就可以继续读取数据了。
接下来 barrier 传递到了内部的 Window 算子,它同样会对自己的状态进行快照保存,写 入远程的持久化存储。
(3)Sink 任务开启事务, 进行预提交
在这里插入图片描述
如图 10- 17 所示,分界线(barrier)终于传到了 Sink 任务,这时 Sink 任务会开启一个事 务。接下来到来的所有数据, Sink 任务都会通过这个事务来写入 Kafka。这里 barrier 是检查点 的分界线, 也是事务的分界线。由于之前的检查点可能尚未完成, 因此上一个事务也可能尚未 提交; 此时 barrier 的到来开启了新的事务,上一个事务尽管可能没有被提交,但也不再接收 新的数据了。
对于 Kafka 而言, 提交的数据会被标记为“未确认”(uncommitted)。这个过程就是所谓 的“预提交”(pre-commit)。
(4)检查点保存完成,提交事务
当所有算子的快照都完成,也就是这次的检查点保存最终完成时, JobManager 会向所有任务发确认通知,告诉大家当前检查点已成功保存,如图 10- 18 所示。
在这里插入图片描述
当 Sink 任务收到确认通知后,就会正式提交之前的事务, 把之前“未确认”的数据标为 “已确认”,接下来就可以正常消费了。
在任务运行中的任何阶段失败, 都会从上一次的状态恢复,所有没有正式提交的数据也会 回滚。这样, Flink 和 Kafka 连接构成的流处理系统,就实现了端到端的 exactly-once 状态一致性。

  1. 需要的配置
    在具体应用中, 实现真正的端到端 exactly-once,还需要有一些额外的配置:
    (1)必须启用检查点;
    (2)在 FlinkKafkaProducer 的构造函数中传入参数 Semantic.EXACTLY_ONCE;
    (3)配置 Kafka 读取数据的消费者的隔离级别
    这里所说的 Kafka,是写入的外部系统。预提交阶段数据已经写入,只是被标记为“未提 交”(uncommitted),而 Kafka 中默认的隔离级别 isolation.level 是 read_uncommitted,也就是 可以读取未提交的数据。这样一来, 外部应用就可以直接消费未提交的数据,对于事务性的保 证就失效了。所以应该将隔离级别配置
    为 read_committed,表示消费者遇到未提交的消息时,会停止从分区中消费数据, 直到消 息被标记为已提交才会再次恢复消费。当然, 这样做的话, 外部应用消费数据就会有显著的延 迟。
    (4)事务超时配置
    Flink 的Kafka 连接器中配置的事务超时时间transaction.timeout.ms 默认是 1 小时,而Kafka 集群配置的事务最大超时时间 transaction.max.timeout.ms 默认是 15 分钟。所以在检查点保存 时间很长时,有可能出现 Kafka 已经认为事务超时了,丢弃了预提交的数据;而 Sink 任务认 为还可以继续等待。如果接下来检查点保存成功, 发生故障后回滚到这个检查点的状态,这部 分数据就被真正丢掉了。所以这两个超时时间,前者应该小于等于后者。
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值