关于--Flink的从Kafka中读取再写入Kafka如何实现的ExactlyOnce的理解

要保证我将数据flush到Kafka的Broker中n条,我的偏移量记录的state就要保存的第n条

如果有10条数据,现在statebacked保存的偏移量状态是5,如果在第7条数据时候超过规定的大小进行了flush,如果不使用kafka的事务,那么,这三条数据就算是真真正正的写入到kafka中了,但是此时我程序在还没有执行下一次checkpoint的时候挂掉了,这时会从statebacked恢复记录偏移量的state中读取偏移量5,从第5条重新读,然后再消费一次这三条数据,这样的话,这三条数据就在kafka中被记录了2次,这样是不行的

要保证我偏移量的state,保存到了statebacked中,才让这一次send中的数据真正保存到kafka

也就是所有subtask的这次checkpoint都完成了,才提交事务,让数据真正写入kafka


经过上面的分析,如果要保证ExactlyOnce,那么就要开启事务,那么在什么时候开启事务呢?

首先可以肯定的是,我要在checkpoint结束(记录偏移量的状态保存成功)后再提交事务,让数据写入kafka,这样才能真真正正的保证记录了偏移量不会重复消费,就算它提前因为达到设定大小flush到kafka中,也是uncommitted状态的数据,所以要在,JobManager集齐了所有subtask的Ack应答之后,再提交事务,这时就要让类实现CheckpointListener接口,来监听JobManager在checkpoint完成后发送的消息,在重写的notifyCheckpointComplete(Checkpoint完成时会调用)方法中,提交事务

那开启事务呢?

应该在生命周期initializeState方法中开启,并在每一次马上要进行checkpoint的时候再一次提交事务,保证每一次checkpoint结束都有立刻有事务来支持下一次的flush数据,来保证数据,不会因为程序突然挂掉,没有保存偏移量,而flush过去产生的脏数据

这样,我第一次checkpoint的时候用是生命周期initializeState方法中的事务,第二次和之后使用snapshotState中开启的事务


现在已经知道要在什么时候开启事务,什么时候提交事务

那应该在什么时候flush呢?

在checkpoint的时候预先flush,这样就算checkpoint失败了,我flush过去的数据,因为没有提交事务,也是uncommitted状态的数据,不会因为重新读偏移量,再次flush过去的时候出现重复的现象

随后,在notifyCheckpointComplete方法中,提交事务,并再次flush,这样组成的两阶段提交接函数,就可以保证ExactlyOnce


部分代码的理解

以下是TwoPhaseCommitSinkFunction中用到的成员变量

 


一、在TwoPhaseCommitSinkFunction类中重写的snapshotState方法--下图

把客户端积攒的数据,预提交到Kafka,因为没有提交事务,所以此时是uncommitted状态

把这次checkpoint的id作为Key、currentTransactionHolder作为Value

put到pendingCommitTransactions这个LinkedHashMap中

开启一个新的支持事务的数据持有者,用来存放之后的数据

把state中的数据clear掉

向state中添加新的数据,这三个传入参数的意义:

        1、新开启的支持事务的数据持有者,这个持有者中还没有数据

        2、将pendingCommitTransactions的Value转换成List,也就是在上面put进去的准备提交事务的数据持有者,数据持有者里面是存放有这个subtask从上一次checkpoint到现在的数据(这里我有一个疑问,为什么他只put了一次,用的还是集合,用一个封装类不行吗,而且一个subtask只有在checkpoint的时候才会调用这方法,我想不到会有put两次的情况)

        3、这个放入的数据,我不清楚,语义好像是当前用户的上下文


 二、在TwoPhaseCommitSinkFunction类中重写的initializeState方法--下图

当subtask启动或重启的时候,会执行这个方法

首先是恢复状态state

判断状态是否恢复完成

恢复完成

        循环state中的数据(我想了很多可能性,这个state中一直只有一个条数据,每次保存到statebackend中的也只有一条,每次恢复只会恢复一条)

                获得数据中的pendingCommitTransactions(也就是之前在snapshotState方法中放入的准备提交事务的数据持有者),赋值给变量recoveredTransactions

                创建一个ArrayList,长度为recoveredTransactions长度+1(这个list我不清楚是做什么的)

                        循环recoveredTransactions

                                调用recoverAndCommitInternal方法,将准备提交事务的数据持有者传入(这个方法就是把准备提交事务的数据持有者的事务提交)

经过上面的步骤,当一个subtask的snapshot state成功,但因为别的subtask的snapshot state失败,而被重启时,也就是提交事务失败(可能是其中一个subtask做快照失败,也可能是其他原因,总之就是snapshot state快照成功了,但没有执行事务提交,就被重启了)而重启时,就可以从state这个状态里面,将之前没有提交的事务,提交,因为之前已经预提交到kafka中了,所以这次就相当于是执行了notifyCheckpointComplete方法(自己的理解,有错误请指正。。)

 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值