写点什么

面试官:怎么保证 Kafka 的消息不丢失

  • 2023-05-24
    湖南
  • 本文字数:2143 字

    阅读完需:约 7 分钟

这篇文章来聊一聊怎么保证 Kafka 的消息不丢失.首先,我们可以反向思考一下:Kafka 什么情况下消息会丢失。本文将从broker生产者还有消费者三个端来讨论一下什么情况下消息会丢失,再通过给出解决方案来处理这些丢失的情况。

broker 端

丢失消息的情况

首先最容易想到的是 broker 可能丢失消息.当消息到达 broker 时,消息可能因为以下几种情况丢失:

  • 消息被 broker 接收到了,但是没有刷到磁盘中,此时断电,,消息丢失了

  • 假如 broker A 是某个分区的首领,broker B 和 broker C 对这个分区进行复制。因为复制会有延时,如果在复制过程中,B 和 C 没有取得 A 的所有消息,此时 A 因为故障宕机了,并且宕机时间非常长,此时 B 或 C 可能会成为该分区的首领,但是因为没有复制到 A 所有的数据,那么在 B 和 C 的角度上看,因为复制延时而没有获取到的数据就丢失了


这两种情况看起来还是比较普遍的。

解决办法

针对第一种情况,我们可以让 broker 将消息刷新到磁盘时,才认为是消息接收成功。如果没有刷新到磁盘,就告诉生产者消息并没有接收成功,需要进行重试,直到消息最终能刷新到磁盘。


针对第二种情况,我们首先要让有复制延时的副本无法成为首领,这种副本在术语上被称为unclean replica,反之没有复制延时的副本叫做in sync replica,也被简称为ISR。当然这里有不严谨的地方,复制通常都是有一定延时的,因为复制消息本身就需要时间,Kafka 提供了一些标准来判断是否一个副本是 ISR。比如在 10s 内是否向分区首领复制过消息,比如在 5s 内是否向 zookeeper 发送过心跳,如果可以满足这些标准,我们就认为是没有延时的。


Kafka 提供了 broker 的配置参数unclean.leader.election.enable=false来实现让不是 ISR 的副本无法成为首领,尽管其他 ISR 还是有可能没有复制到一部分消息,然后因为首领宕机后,某个 ISR 选举成新的首领,但是这种丢失消息的情况概率很低,所以我们认为它属于不丢失消息的情况(ps:如果要硬说,那确实会丢,但这是没有办法的办法,因为主从复制本来就很有可能丢失消息,除非我们再加入其它机制去实现)。


另外,为了消息有更多的副本,Kafka 提供了配置参数min.insync.replicas=N来指定当至少集群有 N 个 ISR(包括首领在内)时才能为生产者提供写服务,但此时可以给消费者提供读服务,可以理解成当 ISR 小于 N 时,分区是只读的。


一些副本可能因为发生网络分区或宕机而没有进行正常的复制,进而变成不同步的副本,这个机制使得我们可以等待这些副本在重启或网络通畅之后去复制落后于首领的那些消息,进而重新成为 ISR。然后整个集群满足 N 个 ISR 后重新提供写服务,因此这个机制可以保证消息有多个副本,进而降低消息丢失的可能性。

生产者端

丢失消息的情况

生产者作为生产消息的一方,看起来好像不太可能丢失消息,但还是存在丢失消息的可能性:

  • 消息发送时发生错误,比如当消息发送到 broker 时,此时正在进行分区的领导选举,broker 返回leader is not available错误,但是由于没有去处理这个错误,误以为消息发送成功,导致消息丢失了

  • 消息发送后,没有得到 broker 的正确响应就认为消息已经发送成功,实际上 broker 并没有接收到,或者并没有刷新到磁盘中

解决办法

其实上面的两个问题最终的现象就是没有得到 broker 的正确响应,所以我们可以统一解决。在生产者端提供了一个 ack 参数,可以确保消息被多少个 broker 处理.ack 有以下几种情况:

  • ack=0 消息只要从网络上发出去,就认为成功

  • ack=1 分区首领接收到消息

  • ack=N(N>0) 分区首领接收到消息后,至少包括首领在内的 N 个副本获取到消息(其余副本通过复制消息得到)

  • ack=all all 等于 broker 设置的 min.insync.replicas 的值,比如 min.insync.replicas=3,那么 ack=3,也就是上面的 N 等于 3 的情况


首先,我们应该至少保证 ack 等于 1,这样至少分区的首领可以接收到消息。我们也可以把 ack 设为更大的数。确保消息被复制多份再返回成功。尽管依赖 broker 之间的复制以及 broker 端的参数也能保证消息有多个副本,但是通过 ack 我们可以强制要求有 N 个副本才能返回成功,进而在生产者端就能知道消息已经复制多份,但这样做的代价是等待这个复制过程是需要时间的,所以性能并不高.如果设置了 ack 之后,broker 无法正确的响应,们就需要不断的重试。当然在实际业务中,我们要想办法设计更加合理的重试机制。

消费者端

丢失消息的情况

消费者可能丢失消息的情况跟 broker 并没有任何关系,主要原因是消费者自己的问题,比如:

  • 消费者拉取消息后,处理消息失败,但提交了偏移量,这视为丢失消息的一种情况。比如在处理消息时,把消息解析后,插入到数据库中发生异常,但提交了最后一个偏移量,下次轮询无法再获取到那些处理失败的消息。

解决办法

这种情况解决的方式其实比较简单,主要就是要确保拉取到的消息要全部处理成功再提交偏移量,比如我们可以针对处理失败的消息进行重试,如果考虑到性能,可以把失败的记录存储起来,先提交偏移量,后面再单独处理这些失败的消息,比如把失败的消息放入一个死信队列,然后通过另外一个专门的消费者不断的重试去消费这些特殊的消息。

消息不丢失是多端的责任

从上面的介绍上看,我们可以看出,消息可能在生产者、broker、消费者这三者中发生丢失的可能,所以丢失是多端的责任,当消息发生丢失,我们可以顺着生产者到 broker,再到消费者这条链路去找到消息丢失的原因。


作者:JL8

链接:https://juejin.cn/post/7236354905493209145

来源:稀土掘金

用户头像

还未添加个人签名 2021-07-28 加入

公众号:该用户快成仙了

评论

发布
暂无评论
面试官:怎么保证Kafka的消息不丢失_Java_做梦都在改BUG_InfoQ写作社区