如何保证 Kafka 消息不丢失? | 木戈手机站

木戈手机站

当前位置: 首页 » 攻略 » 如何保证 Kafka 消息不丢失?

如何保证 Kafka 消息不丢失?

消息队列技术模块在面试中占有重要地位,尽管在面试题中的占比不及并发编程和数据库,但也是面试中的关键问题。今天我们将讨论一道关于消息队列(MQ)的高频面试题,可能会打破你以往的认知。

所谓的关键问题指的是这道面试题会影响你整体面试结果。

在面试消息队列(Message Queue,MQ)时,尤其是面试 Kafka 时,经常会被问到:


如何保证消息不丢失?

那么,我们的回答会分为以下 3 部分:

  1. 保证

    生产者消息不丢失

  2. 保证

    Kafka 服务(服务器端)消息不丢失

  3. 保证

    消费者消息不丢失

只有保证这 3 部分消息都不丢失,才能保证 Kafka 整体消息不丢失。

因为 Kafka 消息的传递流程如下(总共包含 3 部分):

1.如何保证生产者消息不丢失?

那怎么保证生产者消息不丢失呢?

要搞明白这个事,我们就要先了解一下生产者发送消息的执行流程。

Kafka 生产者发送消息的执行流程如下:

默认情况下,所有的消息会先缓存到 RecordAccumulator 缓存中,再由 Sender 线程拉取消息发送到 Kafka 服务器端,通过 RecordAccumulator 和 Sender 线程的协作,实现了消息的批量发送、性能优化和异常处理等功能,确保了消息的高效可靠传输。

1.1 RecordAccumulator 缓存作用


  1. 暂存消息

    :RecordAccumulator 是 Kafka 生产者中的一个关键组件,它充当了一个缓存的角色,用于暂存主线程(Main Thread)发送过来的消息。这些消息在 RecordAccumulator 中等待被 Sender 线程批量发送。

  2. 批量发送

    :RecordAccumulator 通过批量收集消息,减少了单个消息发送的网络请求次数,从而提高了发送效率。Sender 线程可以从 RecordAccumulator 中批量获取消息,一次性发送到 Kafka 集群,减少了网络传输的资源消耗。

  3. 性能优化

    :RecordAccumulator 的缓存大小可以通过生产者客户端参数 buffer.memory 进行配置(默认值为 32MB)。合理的缓存大小设置可以平衡内存使用与发送效率,达到最优的性能表现。

  4. 内存管理

    :如果 RecordAccumulator 的缓存空间被占满,生产者再次调用 send() 方法发送消息时,会出现阻塞(默认阻塞时间为 60 秒,可通过 max.block.ms 参数配置)。如果阻塞超时,则会抛出异常。这种机制有助于防止生产者因为无限制地缓存消息而耗尽系统资源。

  5. ByteBuffer 复用

    :为了减少频繁创建和释放 ByteBuffer 所造成的资源消耗,RecordAccumulator 内部还维护了一个 BufferPool,用于实现 ByteBuffer 的复用。特定大小的 ByteBuffer 会被缓存起来,以便后续消息发送时重复使用。

1.2 Sender 线程作用


  1. 拉取消息

    :Sender 线程是 Kafka 生产者中的一个后台线程,它负责从 RecordAccumulator 中拉取缓存的消息。Sender 线程会定期轮询 RecordAccumulator,检查是否有新消息需要发送。

  2. 批量构建请求

    :当 Sender 线程发现有新消息需要发送时,它会构建一个或多个 ProducerRequest 请求。每个请求包含多个消息,以便进行有效的批量发送。这种批量发送机制可以显著提高网络传输效率。

  3. 发送消息到 Kafka 集群

    :Sender 线程将构建的 ProducerRequest 请求发送到 Kafka 集群的相应分区。它会根据分区的 Leader 节点信息,将消息发送给对应的 Broker 节点。

  4. 异常处理

    :在消息发送过程中,可能会出现网络故障、分区不可用等异常情况。Sender 线程负责处理这些异常,例如进行重试、重新连接等操作,以确保消息的可靠发送。

  5. 状态更新

    :一旦消息被成功接收并记录在 Kafka Broker 的日志中,Sender 线程会通知 RecordAccumulator 更新消息的状态。这样,生产者就能够知道哪些消息已经被成功发送,哪些消息还需要重试发送。

2.生产者消息丢失的两种场景

了解了 Kafka 生产者发送消息的流程之后,我们就能知道在这个环节丢失消息的情况有以下两种:


  1. 网络抖动(消息不可达)

    :生产者与 Kafka 服务端之间的链路不可达,发送超时。此时各个节点的状态是正常,但消费端就是没有消费消息,就像消息丢失了一样。

  2. 无消息确认(ack)

    :生产者消息发送之后,无 ack 消息确认,直接返回消息发送成功,但消息发送之后,Kafka 服务宕机或掉电了,导致消息丢失。

怎么解决这个问题呢?

2.1 网络波动问题处理

网络波动的话设置消息重试即可,因为网络抖动消息不可达,所以只要配置了重试次数,那么就会消息重试以此来保证消息不丢失。

在 Spring Boot 项目中,只需要在配置文件 application.yml 中,设置生产者的重试次数即可:

spring:  
  kafka:  
    producer:  
      retries: 3

2.2 消息确认设置

Kafka 生产者的 ACK(Acknowledgment)机制是指生产者在发送消息到 Kafka 集群后,等待确认的方式。这个机制决定了生产者何时认为消息已经成功发送,并直接影响到消息的可靠性和性能。

Kafka 生产者的 ACK 机制主要有以下三种类型。

① acks=0

生产者在将消息发送到网络缓冲区后,立即认为消息已被提交,不会等待任何来自服务器的响应。这时设置的重试次数 retries 无效。


特点


  • 最高性能

    :由于不需要等待任何确认,因此具有最高的吞吐量。

  • 最低可靠性

    :消息可能会在发送过程中丢失,生产者无法知道消息是否成功到达服务器。


适用场景

:对消息可靠性要求不高,但追求极致性能的场景。

② acks=1

生产者在将消息发送到主题的分区 leader 后,等待 leader 的确认,即认为消息已被提交(此时 leader 写入成功,并没有刷新到磁盘),不用等待所有副本的确认。


特点


  • 中等可靠性和性能

    :提供了一定程度的可靠性,因为只有领导者副本确认消息后生产者才会收到确认。但如果领导者副本在确认后发生故障,而消息还未复制到其他副本,则消息可能会丢失。

  • 性能与可靠性平衡

    :在生产者性能和消息可靠性之间提供了一个折衷方案。


适用场景

:适用于传输普通日志,允许偶尔丢失少量数据的场景。

③ acks=all 或 acks=-1

生产者需要等待所有同步副本(ISR, In-Sync Replicas)都成功写入消息后,才认为消息已被提交。


特点


  • 最高可靠性

    :只有当所有同步副本都确认接收到消息后,生产者才会收到确认,确保了消息的可靠性。

  • 较低性能

    :由于需要等待所有同步副本的确认,因此可能会导致消息发送的延迟增加,从而影响性能。


适用场景

:适用于对消息可靠性要求极高的场景,如金融交易等关键任务应用。

在 Spring Boot 项目中,acks 可以在配置文件 application.yml 中设置:

spring:  
  kafka:  
    producer:  
      acks: all

3.acks=all消息一定不会丢失吗?

正常情况下当我们设置 acks=all 时,其实是可以保证数据不丢失了。但是

有一种特殊情况,如果 Topic 只有一个 Partition(分区时),也就是只有一个 Leader 节点时,此时消息也是会丢失的

如果只有一个 Leader 节点,acks=all 的设置和 acks=1 的设置效果基本类似,当 Leader 确认消息之后,还没来得及将消息刷到磁盘之前宕机了,那么就会造成消息丢失。

万事必有妖,当面试官用疑问语句问你时,答案基本是否定的。如果是确定的话,面试官可能也就不会再问你了,所以当你听到一个有悖于常识的问题时,先努力思考这个问题还有没有其他答案。

课后思考

Kafka 服务器端和消费者如何保证消息不丢失呢?

本文已收录到我的面试小站 www.javacn.site ,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。

猜你喜欢
本类排行