介绍


在0.11版本之后,Kafka基于幂等性引入了事务的支持.事务可以保证Kafka在Exactly Once语义的基础上,生产和消费可以跨分区和会话,要么全部成功,要么全部失败。

kafka事务依赖于Exactly Once,如果你消息都不能保证精准生产,那么也就不能保证Kafka事务了.

Kafka事务分两个,一个 是producer到broker ,另一个是broker到consumer.一般说Kafka事务都是producer到broker,你只要producer到broker保证消息能够不丢失不重复的写入,那就ok了,因为你消息持久化到broker里面之后consumer是pull拉取的, broker对consumer控制是薄弱的 必须你consumer自己控制自己的消费行为才能达到事务的效果.

生产者发送一条消息的时候, broker集群会给生产者一个pid ,这个pid类似于身份证.
broker集群也会记录一下自己颁发的pid.

为什么要用Kafka事务

因为业务逻辑原因,一个方法发送多个消息分别到不同的topic,要求,必须同时成功或者同时失败, 不能说我发送到topicA成功了 ,然后发送到topicB失败了,这样是不行的. 而kafka事务就是即使你topicB发送失败了,此时也会让topicA 回滚,不让consumer消费到topicA.

比如说我topicA是生成订单业务, topicB是扣减库存业务.此时我发送一个消息到topicA,同时发送另外一个消息到topicB , 因为业务场景,生成订单和扣减库存必须同时通知consumer去处理.

Producer事务原理


为了实现跨分区跨会话的事务,需要引入一个全局唯一的Transaction ID,并将Producer获得的PID和Transaction ID绑定,Transaction Coordinator将Transaction ID和PID一起写到Transaction State 的内部topic中。这样当Producer重启后就可以通过正在进行的Transaction ID获得原来的PID,即使整个服务重启,由于事务状态得到保存,进行中的事务状态可以得到恢复,从而继续进行。

假设任务挂掉了,重启broker之后Transaction ID 没有变化,broker会中Transaction ID里面获取之前的任务状态(就是上一次的pid和目前的任务状态),可以实现我们的信息的完全的断点续传,也就是说你Kafka宕机重启之后你信息和你宕机之前的信息是完全一样的.

Consumer事务原理(精准一次性消费)


上述事务机制主要是从Producer方面考虑,对于Consumer而言,事务的保证就会相对较弱,尤其时无法保证Commit的信息被精确消费。
consumer从broker拉取数据之后怎么消费其实是consumer说了算的,consumer拉数据不满意还可以接着拉,broker几乎没有对consumer有控制事务的能力.
consumer消费消息可能由于业务逻辑问题,回滚重头消费,可能会出现第一条消息由于生命周期到了被Kafka删除掉了(这是由于Consumer可以通过offset访问任意信息,而且不同的Segment File生命周期不同,同一事务的消息可能会出现重启后被删除的情况。),有这种风险,所以consumer的回滚是没法精确回滚的.Kafka消息队列是有时效性的.你回滚以后你发现不能精确回滚.所以consumer端的事务我基本不会考虑,

如果想完成Consumer端的精准一次性消费,那么需要kafka消费端将消费过程和提交offset过程做原子绑定。此时我们需要将kafka的offset保存到支持事务的自定义介质中(比如mysql)。

事务代码

https://www.yuque.com/docs/share/95009008-8484-468a-bdd7-23cca6024f3c?# 《事务案例》