消息队列是面试中一定会被问到的技术模块,虽然它在面试题占比不及并发编程和数据库,但也属于面试中的关键性问题。所以今天我们就来看一道,MQ 中高频,但可能会打破你以往认知的一道面试题。
所谓的关键问题指的是这道面试题会影响你整体面试结果。
我们在面试消息队列(Message Queue,MQ)时,尤其是面试 Kafka 时,经常会被问到:如何保证消息不丢失?
那么,我们的回答会分为以下 3 部分:
只有保证这 3 部分消息都不丢失,才能保证 Kafka 整体消息不丢失。
因为 Kafka 消息的传递流程如下(总共包含 3 部分):
那怎么保证生产者消息不丢失呢?
要搞明白这个事,我们就要先了解一下生产者发送消息的执行流程。
Kafka 生产者发送消息的执行流程如下:
默认情况下,所有的消息会先缓存到 RecordAccumulator 缓存中,再由 Sender 线程拉取消息发送到 Kafka 服务器端,通过 RecordAccumulator 和 Sender 线程的协作,实现了消息的批量发送、性能优化和异常处理等功能,确保了消息的高效可靠传输。
了解了 Kafka 生产者发送消息的流程之后,我们就能知道在这个环节丢失消息的情况有以下两种:
怎么解决这个问题呢?
网络波动的话设置消息重试即可,因为网络抖动消息不可达,所以只要配置了重试次数,那么就会消息重试以此来保证消息不丢失。
在 Spring Boot 项目中,只需要在配置文件 application.yml 中,设置生产者的重试次数即可:
spring:
kafka:
producer:
retries: 3
Kafka 生产者的 ACK(Acknowledgment)机制是指生产者在发送消息到 Kafka 集群后,等待确认的方式。这个机制决定了生产者何时认为消息已经成功发送,并直接影响到消息的可靠性和性能。
Kafka 生产者的 ACK 机制主要有以下三种类型。
生产者在将消息发送到网络缓冲区后,立即认为消息已被提交,不会等待任何来自服务器的响应。这时设置的重试次数 retries 无效。
特点:
适用场景:对消息可靠性要求不高,但追求极致性能的场景。
生产者在将消息发送到主题的分区 leader 后,等待 leader 的确认,即认为消息已被提交(此时 leader 写入成功,并没有刷新到磁盘),不用等待所有副本的确认。
特点:
适用场景:适用于传输普通日志,允许偶尔丢失少量数据的场景。
生产者需要等待所有同步副本(ISR, In-Sync Replicas)都成功写入消息后,才认为消息已被提交。
特点:
适用场景:适用于对消息可靠性要求极高的场景,如金融交易等关键任务应用。
在 Spring Boot 项目中,acks 可以在配置文件 application.yml 中设置:
spring:
kafka:
producer:
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、设计模式、消息队列等模块。