当前位置: 首页 > article >正文

【面试题】MQ部分[2025/1/13 ~ 2025/1/19]

MQ部分[2025/1/13 ~ 2025/1/19]

  • 1. 如何处理重复消息?
  • 2. 如何保证消息的有序性?
  • 3. 如何处理消息堆积?
  • 4. 如何保证消息不丢失?
  • 5. [RabbitMQ] RabbitMQ 怎么实现延迟队列?
  • 6. [RabbitMQ] RabbitMQ 中消息什么时候会进入死信交换机?
  • 7. [RabbitMQ] RabbitMQ 中无法路由的消息会去到哪里?
  • 8. [RocketMQ] 为什么 RocketMQ 不使用 Zookeeper 作为注册中心呢?而选择自己实现 NameServer?
  • 9. [RocketMQ] 说一下 RocketMQ 中关于事务消息的实现?
  • 10. [RocketMQ] RocketMQ 的事务消息有什么缺点?你还了解过别的事务消息实现吗?
  • 11. [Kafka] Kafka为什么要抛弃 Zookeeper?
  • 12. [Kafka] Kafka 中 Zookeeper 的作用?
  • 13. [Kafka] 说一下 Kafka 中关于事务消息的实现?

我的博客地址

1. 如何处理重复消息?

只有让消费者的处理逻辑具有幂等性,保证无论同一条消息被消费多少次,结果都是一样的,从而避免因重复消费带来的副作用。

2. 如何保证消息的有序性?

  1. 单一生产者和单一消费者:
    • 使用单个生产者发送消息到单个队列,并由单个消费者处理消息。这样可以确保消息按照生产者的发送顺序消费。
    • 这种方法简单但容易成为性能瓶颈,无法充分利用并发的优势。
  2. 分区与顺序键(Partition Key):
    • 在支持分区(Partition) 的消息队列中(如 Kafka、RocketMQ),可以通过 Partition Key 将消息发送到特定的分区。每个分区内部是有序的,这样可以保证相同 Partition Key 的消息按顺序消费。
    • 例如,在订单处理系统中,可以使用订单号作为 Partition Key,将同一个订单的所有消息路由到同一个分区,确保该订单的消息顺序。
  3. 顺序队列(Ordered Queue):
    • 一些消息队列系统(如 RabbitMQ)支持顺序队列,消息在队列中的存储顺序与投递顺序一致。如果使用单个顺序队列,消息将按顺序被消费。
    • 可以使用多个顺序队列来提高并发处理能力,并使用特定规则将消息分配到不同的顺序队列中。

3. 如何处理消息堆积?

消息堆积是指在消息队列中,消息的生产速度远大于消费速度,导致大量消息积压在队列中。

  1. 消费者: 提升消费者的消费能力, 批量消费和异步消费
    • 增加消费者线程数量:提高并发消费能力。
    • 增加消费实例:在分布式系统中,可以水平扩展多个消费实例,从而提高消费速率。
    • 优化消费者逻辑:检查消费者的代码,减少单个消息的处理时间。例如,减少 I/O 操作、使用批量处理等。
  2. 生产者:
    生产端限流, 使用令牌桶、漏桶算法等限流策略,限制生产者的发送速率,从而避免消息队列被快速填满。

4. 如何保证消息不丢失?

主要从生产者broker消费者:

  • 生产者的消息确认:生产者在发送消息时,需要通过消息确认机制来确保消息成功到达。
  • 存储消息:broker 收到消息后,需要将消息持久化到磁盘上,避免消息因内存丢失。即使消息队列服务器重启或宕机,也可以从磁盘中恢复消息。
  • 消费者的消息确认:消费者在处理完消息后,再向消息队列发送确认(ACK),如果消费者未发送确认,消息队列需要重新投递该消息。

5. [RabbitMQ] RabbitMQ 怎么实现延迟队列?

  1. TTL+DLX
    RabbitMQ 本身不支持延迟消息,但是可以通过它提供的两个特性 TTL(Time-To-Live and Expiration ,消息存活时间)、DLX(Dead Letter Exchanges,死信交换器) 来实现。

    注: ttl+dlx问题: 队列是先进先出, 当前面消息时间过长,会导致后续消息延迟问题, 出现时序问题

  2. 利用RabbitMQ提供插件: delay_message_exchange

6. [RabbitMQ] RabbitMQ 中消息什么时候会进入死信交换机?

  1. 消息被拒绝(Rejection)
    当消费者使用 basic.reject 或 basic.nack 明确拒绝消息,并且不要求重新投递(requeue 设置为 false)时,消息会被直接投递到死信交换机。
  2. 消息过期(TTL Expiration)
    RabbitMQ 支持为消息或队列设置 TTL(Time-To-Live),即生存时间。当消息超过指定的存活时间后还未被消费,它会自动变为死信并被发送到死信交换机。
  3. 队列达到最大长度(或总大小)
    如果队列设置了最大长度(x-max-length 或 x-max-length-bytes),当消息数量或总大小超出限制时,最早进入队列的消息会被移入死信交换机。

7. [RabbitMQ] RabbitMQ 中无法路由的消息会去到哪里?

  1. 丢弃消息
    默认情况下,若消息无法找到符合条件的队列(即没有匹配的绑定关系),RabbitMQ 会直接丢弃消息,不会进行特殊处理。
  2. 备份交换机(Alternate Exchange)
    可以为交换机配置一个备份交换机,无法被路由的消息将被发送到备份交换机,再由备份交换机根据其绑定关系决定如何处理消息。例如,可以将这些消息发送到指定队列进行保存或处理。
  3. 消息回退(Return Listener)
    在使用 mandatory 参数的情况下,如果消息无法路由,则会触发返回机制,将消息退回到生产者,这样生产者可以自行处理未路由的消息。

8. [RocketMQ] 为什么 RocketMQ 不使用 Zookeeper 作为注册中心呢?而选择自己实现 NameServer?

  1. 轻量级和高性能需求
    RocketMQ 需要一个高性能、低延迟的注册中心。Zookeeper 的强一致性和复杂的管理机制带来了一定的开销。NameServer 是一个简单的、轻量级的服务发现和路由管理组件,更适合于消息队列的需求。
  2. 动态拓扑和高扩展性
    NameServer 采用无状态设计,可以随时增加或减少节点,且 NameServer 节点间不需要进行同步,减少了集群复杂度,适应动态的 Broker 集群环境,便于拓展和维护。
  3. 弱一致性要求
    RocketMQ 的服务发现和路由信息允许短时间内的不一致,NameServer 只提供基础的服务发现功能,不需要像 Zookeeper 那样强一致的分布式一致性算法,这样可以提高性能和减少管理复杂度。

9. [RocketMQ] 说一下 RocketMQ 中关于事务消息的实现?

  1. 第一阶段(消息发送)
    • 生产者先将消息发送到 RocketMQ 的 Topic,此时消息的状态为半消息(Half Message),消费者不可见。
    • 然后,生产者执行本地事务逻辑,并根据本地事务的执行结果来决定下一步的操作。
  2. 第二阶段(提交或回查)
    • 如果本地事务成功,生产者会向 RocketMQ 提交 Commit 操作,将半消息变为正式消息,消费者可见。
    • 如果本地事务失败,生产者会向 RocketMQ 提交 Rollback 操作,RocketMQ 会丢弃该半消息。
    • 如果生产者没有及时提交 Commit 或 Rollback 操作,RocketMQ 会定时回查生产者本地事务状态,决定是否提交或回滚消息。

10. [RocketMQ] RocketMQ 的事务消息有什么缺点?你还了解过别的事务消息实现吗?

  • 从事务消息的改造成本来看,RocketMQ 的事务消息改造成本不小,需要改造原始逻辑实现特定的接口,且在应用层处理复杂的回查逻辑,确保回查不会重复或丢失。
  • 从事务消息功能性来看,RocketMQ 仅支持单事务消息
  • 从可用性角度来看,依赖MQ集群的可靠性。如果MQ 集群挂了,事务就无法继续进行了,等于整个应用无法正常执行了。

其他解决方案 QMQ(Qunar Message Queue)(去哪儿),数据现存在数据库, 同时创建一张消息定时遍历去发送消息

  1. QMQ是什么?
    QMQ(Qunar Message Queue)是由去哪儿网自主研发的一款高性能、低延迟、可靠的分布式消息队列系统。它主要用于解决高并发、高吞吐量的业务场景下的消息传递问题,特别适用于订单处理、支付系统等对实时性和可靠性要求较高的场景。
  2. QMQ的设计思路
    • 分布式架构与去中心化设计
      QMQ采用分布式架构,去中心化设计,避免了单点故障问题,同时支持水平扩展。这种设计使得系统在面对大规模消息处理时更加稳定,且易于扩展。
    • 消息存储与持久化
      QMQ的消息存储采用了顺序写入磁盘的方式,类似于Kafka的日志存储结构。消息按照到达顺序写入Broker的存储文件中,充分利用磁盘的顺序写性能,提升写入速度。此外,QMQ支持消息的多副本存储,确保消息的可靠性。
    • 事务一致性与补偿机制
      QMQ在消息发送时,会先将消息存储在本地磁盘队列中,然后异步发送到Broker。如果发送失败,系统会通过补偿任务重新发送消息,直到成功为止。这种机制确保了消息的可靠性和事务一致性。
    • 动态映射与消费模式
      QMQ通过添加一层拉取日志(pull log)来动态映射消费者与消息的逻辑关系。这种方式解决了消费者动态扩容缩容的问题,同时继续使用单一偏移量(offset)表示消费进度。此外,QMQ支持广播模式和集群模式两种消费模式,分别适用于全量消息消费和负载均衡场景。
    • 高性能与低延迟
      QMQ通过优化的消息序列化和网络通信机制(如Netty),实现了低延迟和高吞吐量。系统还支持异步非阻塞I/O机制,确保在处理大量并发请求时不会因某个操作的延迟而影响整体性能。

11. [Kafka] Kafka为什么要抛弃 Zookeeper?

主要是为了简化架构、提升可扩展性和降低运维复杂性。Kafka 引入了 KRaft(Kafka Raft)模式,即使用 Kafka 自身实现的 Raft 共识算法替代 Zookeeper。

  1. 简化架构:Zookeeper 是一个独立的分布式协调服务,Kafka 需要依赖它来管理元数据。引入 Zookeeper 增加了系统复杂度,尤其在处理集群的动态扩展和管理时,Kafka 和 Zookeeper 之间的协调带来了额外的开销。通过 KRaft,Kafka 可以直接管理元数据,消除了对外部协调服务的依赖。
  2. 提升可扩展性:Zookeeper 的写入性能有限,随着 Kafka 集群的规模增大,元数据的读写操作可能会对 Zookeeper 造成压力,进而成为 Kafka 扩展性的瓶颈。KRaft 模式通过将元数据存储在 Kafka 自身中,并使用 Raft 协议来确保一致性,使 Kafka 的扩展能力得到提升。
  3. 降低运维成本:在生产环境中,Kafka 和 Zookeeper 之间需要进行一致性管理和维护,运维人员需要掌握两套系统的部署、监控和故障排查。去掉 Zookeeper 后,Kafka 只需维护自身的节点和协议,简化了运维流程。

Raft 是一种用于分布式系统中管理复制日志的共识算法,旨在通过清晰的角色划分(Leader、Follower、Candidate)和结构化的选举日志复制流程来简化理解和实现。它确保了即使在网络分区或节点故障的情况下,集群中的多数节点仍能达成一致,从而维持系统的高可用性和数据一致性。Raft 的关键特性包括基于任期的领导选举、安全的日志复制机制以及对新 Leader 的严格限制,这些都保证了系统的稳定运行和数据的完整性。

12. [Kafka] Kafka 中 Zookeeper 的作用?

在 Kafka 中,Zookeeper 扮演了集群协调和管理的核心角色。它的主要作用是管理和协调 Kafka 集群中的元数据,帮助 Kafka 实现高可用性、负载均衡和容错性。

  1. 管理 Broker 元数据
    Zookeeper 负责管理 Kafka 集群中 Broker 的注册、状态监控。当有新的 Broker 加入或离开集群时,Zookeeper 能够及时更新集群状态。
  2. 协调分区副本 Leader 选举
    当某个分区的 Leader 副本故障时,Zookeeper 协调副本的选举过程,为该分区选出新的 Leader,确保分区高可用。
  3. 动态配置和负载均衡
    Zookeeper 保存着 Kafka 配置和拓扑信息,当集群发生变化时(如增加或减少分区、调整副本因子),Zookeeper 协助完成负载均衡。

13. [Kafka] 说一下 Kafka 中关于事务消息的实现?

Kafka 的事务消息不同于我们理解的分布式事务消息,它的事务消息是实现了消息的Exactly Once 语义,即保证消息在生产、传输和消费过程中的“仅一次”传递。

  1. 几个核心组件来实现:

    • 事务协调器(Transaction Coordinator):负责事务的启动、提交和中止管理,并将事务状态记录到 __transaction_state 内部主题。
    • 幂等生产者(Idempotent Producer):Kafka Producer 通过 Producer ID (PID) 识别每个事务的唯一性,确保同一事务的每条消息只写入一次。
    • 事务性消费:在消费过程中,消费者可以选择隔离未完成事务的数据(通过 read_committed 设置),只消费已提交的事务消息,确保数据的最终一致性。
  2. Kafka 事务消息流程:

    • 启动事务:事务性生产者向 Transaction Coordinator 请求启动事务。
    • 生产消息:生产者开始向 Kafka 写入事务消息,每条消息都带有唯一的 Producer ID 和 Sequence Number,以保证幂等性。
    • 提交事务:在所有消息写入完成后,生产者向事务协调器发送 commit 或 abort 请求,提交或中止事务。
    • 事务性消费:消费者可以通过设置 read_committed 隔离级别,仅消费已提交的消息,实现最终数据一致性。

http://www.kler.cn/a/514706.html

相关文章:

  • QT:QTabWidget设置tabPosition为West时,文字向上
  • 网络协议如何确保数据的安全传输?
  • Dart语言的学习路线
  • 微软预测 AI 2025,AI Agents 重塑工作形式
  • Ubuntu22部署MySQL5.7详细教程
  • 【useContext Hook】解决组件树层级较深时props逐级传递问题
  • 【Uniapp-Vue3】StorageSync数据缓存API
  • OGG 19C 集成模式启用DDL复制
  • ORA-15041 ORA-15023
  • Kotlin 2.1.0 入门教程(八)
  • js截取video视频某一帧为图片
  • Kafka 入门与应用实战:吞吐量优化与与 RabbitMQ、RocketMQ 的对比
  • SVM模型实战1
  • RabbitMQ 在实际应用时要注意的问题
  • Qt基础项目篇——Qt版Word字处理软件
  • 代码随想录day15
  • h5页面两个吸顶tab切换第二个tab从头开始显示
  • 手机备忘录:安全存储与管理个人笔记的理想选择
  • 【详解】神经网络的发展历程
  • 基于微信小程序的模拟考试系统设计与实现(LW+源码+讲解)
  • (算法竞赛)DFS深搜3——数池塘问题解析与代码实现
  • SPDK vhost介绍
  • 【2024年华为OD机试】 (E卷,100分) - 路灯照明问题(JavaScriptJava PythonC/C++)
  • 图像处理基础(4):高斯滤波器详解
  • Quick Startup,快捷处理自启程序的工具,加快电脑开机速度!
  • 基于STM32的智能书架管理系统设计