Kafka 数据丢失和重复消费问题

首先介绍下 producer与consumer有可能的delivery guarantee:
At most once 消息可能会丢,但绝不会重复传输
At least one 消息绝不会丢,但可能会重复传输
Exactly once 每条消息肯定会被传输一次且仅传输一次,很多时候这是用户所想要的

一、数据丢失问题:

生产方:
Kafka 数据丢失和重复消费问题_第1张图片

producer 的deliver guarantee 可以通过request.required.acks参数的设置来进行调整:
0 ,相当于异步发送,消息发送完毕即offset增加,继续生产;相当于At most once
1,leader收到leader replica 对一个消息的接受ack才增加offset,然后继续生产;
-1,leader收到所有replica 对一个消息的接受ack才增加offset,然后继续生产
例如设置为0时,当producer向broker发送消息时,producer消息发送完毕即offset增加,遇到的网络问题而造成通信中断,那么数据没有落到broker,同时producer也不会做补偿操作,因此导致生产方的消息丢失。

- 消费方:

1、(重复消费问题)consumer在从broker读取消息后,可以选择commit,该操作会在Zookeeper中存下该consumer在该partition下读取的消息的offset。该consumer下一次再读该partition时会从下一条开始读取。如未commit,下一次读取的开始位置会跟上一次commit之后的开始位置相同。当然可以将consumer设置为autocommit,即consumer一旦读到数据立即自动commit。如果只讨论这一读取消息的过程,那Kafka是确保了Exactly once。但实际上实际使用中consumer并非读取完数据就结束了,而是要进行进一步处理,而数据处理与commit的顺序在很大程度上决定了消息从broker和consumer的delivery guarantee semantic。
2、(数据丢失问题)读完消息先commit再处理消息。这种模式下,如果consumer在commit后还没来得及处理消息就crash了,下次重新开始工作后就无法读到刚刚已提交而未处理的消息,这就对应于At most once
读完消息先处理再commit。这种模式下,如果处理完了消息在commit之前consumer crash了,下次重新开始工作时还会处理刚刚未commit的消息,实际上该消息已经被处理过了。这就对应于At least once(默认)
3、如果一定要做到Exactly once,就需要协调offset和实际操作的输出。精典的做法是引入两阶段提交。如果能让offset和操作输入存在同一个地方,会更简洁和通用。这种方式可能更好,因为许多输出系统可能不支持两阶段提交。比如,consumer拿到数据后可能把数据放到HDFS,如果把最新的offset和数据本身一起写到HDFS,那就可以保证数据的输出和offset的更新要么都完成,要么都不完成,间接实现Exactly once。(目前就high level API而言,offset是存于Zookeeper中的,无法存于HDFS,而low level API的offset是由自己去维护的,可以将之存于HDFS中)

二、重复消费问题:

即上述 消费方第1种情况—consumer在从broker读取消息后等消费完再commit,如果consumer还没来得及消费或消费时crash,导致offset未提交,该consumer下一次读取的开始位置会跟上一次commit之后的开始位置相同,导致重复消费问题。
关于重复消费的问题,可以通过将每次消费的数据的唯一标识存入Redis中,每次消费前先判断该条消息是否在Redis中,如果有则不再消费,如果没有再消费,消费完再将该条记录的唯一标识存入Redis中,并设置失效时间,防止Redis数据过多、垃圾数据问题。

参考资料:
https://blog.csdn.net/wingofeagle/article/details/60965867

你可能感兴趣的:(消息中间件)