由来
今天,我就想看一看kafka的exactly once 相关的知识。随后我百度了几篇文章,都不尽如人意。我想能搜索exactly once 关键词的肯定都是对于kafka有一定的基础了解了吧,结果我搜索出来的都是一些长篇大论,没有主次,不清楚是哪里摘抄的。后面我又看了些视频,才算有了直观认识。
exactly once
在早期版kafka版本上,我们可以设置ack = 1,-1,0 来分别设置producer到broker的发送策略。
- ack = 1 ,producer 等待 broker 的 ack,partition 的 leader 落盘成功后返回 ack,如果在 follower 同步成功之前 leader 故障,那么将会丢失数据。
- ack = -1,producer 等待 broker 的 ack,partition 的 leader 和 follower 全部落盘成功后才返回 ack。但是如果在 follower 同步完成后,broker 发送 ack 之前,leader 发生故障,那么会造成数据重复。满足 at least once。
- ack = 0 ,producer不需要等待ack,直接发送下一次数据。可能造成数据丢失,但是不会重复。满足 at most once。
但是在有些场景下,需要满足这条记录只能被只消费一次。早期版本我们是这么实现的。将ack=-1 满足 at least once,数据可以重复,但是绝对不能丢失。此时我们业务系统将这条消息增加一个requestID,代表请求的唯一ID,如果我们已经处理过这个request了,那么我们业务系统自己实现不重复处理逻辑。
在kafka0.11版本,增加了对exactly once 的支持。其实思路跟我们还是一样的,就是不能重复处理请求数据。那么我们首先要保证at least once 数据不丢失啊,然后这会儿kafka帮我们实现,它在producer初始化时,会分配一个PID,发往同一个partition的消息会再附带一个sequence num 。然后
<PID,partition,sequencenum> 构成了主键,下次如果有相同主键的内容,那么broker就落盘一次。实现了exactly once。
这个功能的开关怎么设置呢,只需要将 Producer 的参数中 enable.idompotence 设置为 true 即可。
提醒一点,因为这个主键中有partition,所以如果第一次你发送失败了,第二次发送到的分区变了,那么就不能保证唯一了。
那,什么情况下回发生分区改变的呢,reblance的时候。如果producer重新发送消息,在此之前进行了reblance,那么这条数据的分区号就改变了,kafka也就无法判断是重复数据了。