kafka异步提交偏移量的问题

关于kafka异步提交偏移量,一直有一个疑问:
就是消费者在一次拉取后,拉取到偏移量为N到N+10的十条消息,并进行消费,之后采用异步提交偏移量N+11。因为是异步提交,kafka客户端继续进行了下一拉取,如果这时候异步提交偏移量是在第二次拉取之后才成功的,那么kafka客户端的第二次拉取不是依然会从偏移量为N的地方拉取消息从而导致重复消费么?

由于consumer在消费过程中可能会出现断电宕机等故障,consumer恢复后,需要从故障前的位置的继续消费,所以consumer需要实时记录自己消费到了哪个offset,以便故障恢复后继续消费
Kafka 0.9版本之前,consumer默认将offset保存在Zookeeper中,从0.9版本开始,consumer默认将offset保存在Kafka一个内置的topic中,该topic为 _consumer_offsets

在这里我们要引入一个叫做hw水位线的概念,注释:leader就是主机,follower就是从机(kafka配置在集群中)

全称 high watermarker 水位线。
ISR列表中,每个写入对应分区中leader副本的数据,follower会拉取数据期望与leader数据进行同步数据和offset,此刻因为网络延迟,会导致不同的follower拉取的速度不一样,在高并发场景下follower通常会滞后于leader,那么ISR内部offset最低的那个值就是HW。
作用:
消费者只能消费HW这个offset以下的数据。

img

其实这种问题,就是考验我们对于消息幂等性的处理方式了,我这里给几个思路:
假如你拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入了,update 一下好吧。
假如你是写 Redis,那没问题了,反正每次都是 set,天然幂等性。
假如你不是上面两个场景,那做的稍微复杂一点,你需要让生产者发送每条数据的时候,里面加一个全局唯一的 id,类似订单 id 之类的东西,然后你这里消费到了之后,先根据这个 id 去比如 Redis 里查一下,之前消费过吗?如果没有消费过,你就处理,然后这个 id 写 Redis。如果消费过了,那你就别处理了,保证别重复处理相同的消息即可。
假如基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据插入只会报错,不会导致数据库中出现脏数据。

所以可以这样理解吧,异步提交偏移量本身就存在可能重复消费的问题,我们得自己去实现消费的幂等性吧