Kafka消息丢失、重复消费的解决方案

生产者问题

Producer发送消息到队列,分区Leader收到消息后返回ACK给Producer,表示接收成功,此时可以继续发送下一笔消息。

Kafka提供了3种不同级别的ACK机制:

  • 0:Leader收到消息后立刻返回给Producer,消息可能还没刷盘,也还没有同步给Follower。此时如果Leader挂掉,消息就丢失了。
  • 1:Leader将消息写入磁盘后,立刻返回ACK给Producer,消息可能还没同步Follower。此时如果Leader挂掉,选举新的Follower成为主分区,因为刚才没同步成功,所以消息丢失。
  • -1:Leader将消息写入磁盘,并且同步给所有Follower,此时再返回ACK。这个方案也存在一个问题,如果在Leader准备返回ACK的时候挂掉,Producer没收到ACK认定为发送失败。此时又有2种情况
    (1)开启重试:会导致队列收到2条重复的消息,此时需要Consumer端保证消费幂等,后面会介绍
    (2)不开启重试(不推荐):网络问题、超时等原因导致发送失败,不会重新发送,消息容易丢失

消费者问题

Consumer进行消费时,提交Offset偏移给Kafka记录消费的最新位置。 提交Offset有以下2种方式:

  • 自动提交:enable.auto.commit 设置 true 开启,auto.commit.interval.ms 设置自动提交的频率间隔。Consumer获取到消息就提交Offset,不管消费是否成功
  • 手动提交:enable.auto.commit 设置 false ,消费完成后再调用 consumer.commit(),手动更新Offset。如果消费失败,我们可以将异常记录进行人工干预,然后再提交Offset,避免影响后面消息的消费。

问题总结

Kafka消息丢失有2个方面:

  • Producer的ACK等级设置0或者1,可能因为没落盘或者没同步Follower导致丢失
  • Consumer设置自动提交,消费失败来不及记录异常就宕机,但是Offset提交了,导致丢失

Kafka重复消费:

  • Producer的ACK设置-1,没收到ACK进行重新发送,导致重复了

解决方案

对性能要求不是极高的情况下,建议将ACK设置-1。
此时,我们只需要在 Consumer 避免消息重复消费就行。

为了避免重复,我们需要在发送消息的时候增加一个唯一的标识字段。例如:订单ID、订单号。 Consumer消费成功后,将该标识记录在Redis等媒介。下次消费的时候,先判断当前标识如果已经存在,直接跳过;如果不存在,再进行消费

在这里插入图片描述
此外,消息标识ID也可以记录在MySQL。建议开启唯一性索引,让MySQL直接帮我们检查,抛出异常就是重复消费,直接结束即可。
在这里插入图片描述

如果对性能要求极高,同时又允许部分数据丢失。可以将ACK等级改成0或者1

例如:发送短信、日志记录等场景。本身Kafka挂掉的几率就不大,就算异常导致个别消息丢失,在这些场合下也无伤大雅。

因此,具体采用哪种方案,大家还是要根据业务进行选择。

猜你喜欢

转载自blog.csdn.net/qq_28834355/article/details/113663832