被怼了:acks=all消息也会丢失?

news/2024/11/21 0:17:20/文章来源:https://www.cnblogs.com/vipstone/p/18345747

消息队列是面试中一定会被问到的技术模块,虽然它在面试题占比不及并发编程和数据库,但也属于面试中的关键性问题。所以今天我们就来看一道,MQ 中高频,但可能会打破你以往认知的一道面试题。

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

我们在面试消息队列(Message Queue,MQ)时,尤其是面试 Kafka 时,经常会被问到:如何保证消息不丢失?

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

  1. 保证生产者消息不丢失
  2. 保证 Kafka 服务(器端)消息不丢失
  3. 保证消费者消息不丢失

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

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

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

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

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

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

1.1 RecordAccumulator 缓存作用

  1. 暂存消息:RecordAccumulator 是 Kafk a生产者中的一个关键组件,它充当了一个缓存的角色,用于暂存主线程(Main Thread)发送过来的消息。这些消息在 RecordAccumulato r中等待被 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、设计模式、消息队列等模块。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/778853.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GaussDB安装

环境准备 1、关闭防火墙 systemctl stop firewalld 2、关闭selinux 临时禁用:setenforce 0 永久关闭: vi /etc/selinux/configSELINUX=disabled reboot 3、修改系统环境字符集 echo $LANG export LANG=en_US.UTF-8 永久修改 vi /etc/profile 添加 export LANG=en_US.UTF-8 so…

lambda 中 map 和 flatMap 的区别

lambda 中 map 和 flatMap 的区别https://blog.csdn.net/weixin_52772307/article/details/128944511总结: 当我们需要将具有层级结构的数据展平时,也就是将多层数据转换为单层数据操作时,我们可以使用 flatMap 方法。如果我们只是简单的对流中的数据计算或者转换时,可以使…

python 音频处理(2)——提取PPG特征之whisper库的使用(2.1)

PPG特征 提取PPG特征 whisper库使用提取PPG特征之——whisper库的使用(2.1) 1 安装对应的包方法一(自用): 直接pip即可: pip install openai-whisper 成功后如下图所示方法二: 当时用了他这个方法环境直接崩了,已老实conda install -c conda-forge ffmpeg conda insta…

数字量输入模块:远程组态说明

XD系列插片式远程 I/O模块是兴达易控技术研发的分布式扩展模块。XD系列成套系统主要由耦合器、各种功能I/O模块、电源辅助模块以及终端模块组成。有多种通讯协议总线的耦合器,例如 PROFINET、EtherCAT、Ethernet/IP、Cclink IE以及modbus/TCP等。I/O 模块可分为多通道数字量输…

Rust_learn_1

变量与可变性 变量 声明变量使用let关键字,在默认情况下,变量是不可变的(Immutable)。为此解决该问题,声明变量时在前面加上 mut,就可以使变量可变常量 常量(constant),在绑定值之后也是不可变的,但是与不可变的变量有很多区别:不可以使用mut,常量永远是不变的声明常…

【python海龟画图】代码整理

春联点击查看代码 import turtle t = turtle t.showturtle() t.penup() t.goto(-150,150) t.pendown()t.color(black, red) t.begin_fill() for i in range(2):t.forward(50)t.right(90)t.forward(400)t.right(90) t.end_fill()t.penup() t.goto(100, 150) t.pendown()t.begin…

【验证码逆向专栏】某安登录流程详解与验证码逆向分析与识别

声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术…

Excel 根据单元格值设置行颜色

开始》条件格式》管理规则》新建格式规则》使用公式确定要设置格式的单元格 只为满足以下条件的单元格设置格式:=SEARCH("进行中",$E5)>0 ;(注释:此处筛选的是包含进行中的数据) 格式:选择满足条件的单元格设置什么格式 对某个单元格设置后【此示例使用的是…

怎么将回滚操作

点击禁用双击 install

RAG技术要点及英智未来的应用实践

RAG是检索增强生成(Retrieval-augmented Generation),概念是在2020年发表的论文《面向知识密集型NLP任务的检索增强生成》中提出的。 LLM的工作原理是基于预训练的历史数据进行推理生成文本,所以LLM在生成回答的时候可能会引入了过期的、不可预测的或者错误的信息,导致大语…

2024暑假集训测试18

前言比赛链接。这次有大量外校人员参加,\(90\) 来个人,T1 胡了个结论上去结果大小样例都过了,造 hack 还没 hack 了,索性交了,但是有捆绑感觉会爆零,没想到结论是对的,直接 A 了;打完 T1 就罚坐了,三个小时就弄出来 \(5\) 分,当时都绝望了,想到了很多东西。因为感觉…