消息队列(面试)

名称 简介
ActiveMQ Apache 出品的老牌消息中间件,支持多种消息模型,传统企业应用常用,高并发时性能一般。
RabbitMQ 基于 AMQP 协议,性能良好,支持多种消息模型,企业级应用常见,社区活跃。
RocketMQ 阿里开源的分布式中间件,高吞吐、低延迟,支持顺序、事务消息,国内互联网行业广泛使用。
Kafka 高吞吐分布式消息系统,适合大数据实时处理,多副本保证可靠性,水平扩展好。

高吞吐量指的是系统在单位时间内能够处理的消息数量非常多。

消息队列使用场景有哪些

解耦:可以在多个系统之间进行解耦,将原本通过网络之间的调用的方式 改为使用MQ进行消息的异步通讯,只要该操作不是需要同步的,就可以改为使用MQ进行不同系统之间的联系,这样项目之间不会存在耦合,系统之间不会产生太大的影响,就算一个系统挂了,也只是消息挤压在MQ里面没人进行消费而已,不会对其他的系统产生影响。

异步:

流量削峰:

消息重复消费怎么解决

核心解决思路是 "让消费逻辑具备幂等性"(即重复执行多次与执行一次结果一致),再结合消息中间件的机制辅助控制。

核心:1. 消息生成全局唯一 ID(如 UUID、雪花 ID);

  1. 消费前先查 "幂等表"(数据库表 / Redis),判断该 ID 是否已消费;

  2. 未消费则执行逻辑,执行后写入幂等表;已消费则直接跳过。

辅助:手动提交 "消费确认"(如 Kafka 的 offset、RabbitMQ 的 ack),避免中间件因未收到确认而重复投递;

消息丢失怎么解决(怎么保证可靠性)

1. 生产端:确保消息成功发送

  • 同步确认机制 :使用消息中间件,等待 Broker 确认消息已持久化后再返回成功,避免网络波动导致的发送丢失。
  • 重试机制:对发送失败的消息(如网络超时),通过有限次数重试(配合指数退避策略)提高成功率,同时记录重试日志便于排查。

2. 中间件:确保消息可靠存储

  • 持久化配置:开启 Broker 的持久化(如 Kafka 的分区副本机制、RabbitMQ 的队列持久化 + 消息持久化),避免 Broker 宕机导致内存中消息丢失。
  • 集群与副本:部署多副本集群,确保主节点故障时,从副本能接管服务并恢复消息。

3. 消费端:确保消息正确处理

  • 手动确认机制:关闭自动确认,在业务逻辑完全处理成功后,再手动发送确认信号。
  • 幂等性处理:通过消息唯一 ID(如 UUID)+ 数据库唯一索引 / 分布式锁,避免因重试导致的重复消费,确保消息处理结果一致。
  • 失败重试与死信队列:消费失败时,将消息暂存至重试队列(有限次重试),最终失败的消息转入死信队列,人工介入处理,避免消息直接丢弃。

消息队列的顺序性怎么保证

顺序性保证(确保消息按发送顺序消费)

1. 生产端:保证消息按顺序发送

  • 对同一业务流(如同一订单的状态变更),通过「分区键」路由到 MQ 的同一个分区 / 队列,确保消息在中间件中物理顺序一致。

2. 中间件:保证消息存储顺序

  • MQ 的单个分区 / 队列内部是天然有序的,因此需避免多分区并行写入破坏顺序。

3. 消费端:保证消息按顺序处理

  • 单线程消费:一个分区 / 队列仅由一个消费线程处理,避免多线程并行执行导致的顺序错乱。
  • 禁止消息跳过:消费失败时,不直接跳过当前消息处理下一条(可通过重试机制等待恢复,或转入死信队列后人工处理)。

消息队列的消息积压怎么解决

消息队列的消息积压通常是由于 "生产速度远大于消费速度" 或 "消费端故障停摆" 导致的

优化一下消费的逻辑 ,比如之前是一条一条消息消费处理的话,我们可以确认是不是可以优为批量处理消息 。如果还是慢,我们可以考虑水平扩容,增加 Topic的队列数 ,和消费组机器的数量,提升整体消费能力。

如何保证数据一致性,事务消息如何实现

为了保证消息的可靠性(比如避免消息丢失),MQ 通常会将消息存储在专门的存储系统中。

我们举个下订单的例子吧。订单系统创建完订单后,再发送ACK消息给下MQ(5)。如果订单创建成功,然后ACK消息没有成功发送出去,MQ就无法感知这个事情,出导致数据不一致。

如何保证数据一致性呢?可以使用事务消息。一起来看下事务消息是如何实现的吧。

事务消息的核心是将本地业务操作(订单创建)和消息发送操作 绑定为一个原子性的事务,确保要么两者都成功,要么都失败

事务消息的实现原理(以主流中间件 RocketMQ 为例)

步骤 1:发送 "预处理消息"
  • 业务发起方(如订单服务)向 RocketMQ 发送一条 "预处理消息",内容为后续需通知接收方(如库存服务)的操作(如 "扣减商品 A 库存 10 件")。
  • RocketMQ 收到消息后,不会直接标记为 "可消费" ,而是先存储在 "事务消息存储区",并返回一个 "消息 ID" 给发起方(此时接收方无法消费该消息)。
步骤 2:执行本地事务(业务操作)
  • 发起方(订单服务)基于返回的 "消息 ID",执行本地核心业务(如创建订单,写入订单表)。
  • 本地事务执行结果只有两种:成功失败
步骤 3:确认 / 回滚消息(事务提交 / 回滚)
  • 若本地事务成功 :发起方向 RocketMQ 发送 "确认消息"(Commit),RocketMQ 将 "预处理消息" 从 "事务存储区" 移到 "普通消息队列",标记为 "可消费",接收方(库存服务)即可消费消息并执行扣库存操作。
  • 若本地事务失败:发起方向 RocketMQ 发送 "回滚消息"(Rollback),RocketMQ 删除 "预处理消息",接收方不会收到任何消息,避免无效操作。
步骤 4:RocketMQ 的 "事务回查"(兜底机制)
  • 若发起方在步骤 3 中因网络故障、服务宕机等原因 ,未向 RocketMQ 发送 "确认 / 回滚" 指令,RocketMQ 会主动发起 "事务回查":
    • 根据回查结果,RocketMQ 自动执行 "确认" 或 "回滚":若订单已创建,则 Commit 消息;若订单未创建,则 Rollback 消息。
相关推荐
Lee川1 天前
优雅进化的JavaScript:从ES6+新特性看现代前端开发范式
javascript·面试
Lee川1 天前
从异步迷雾到优雅流程:JavaScript异步编程与内存管理的现代化之旅
javascript·面试
晴殇i1 天前
揭秘JavaScript中那些“不冒泡”的DOM事件
前端·javascript·面试
绝无仅有1 天前
Redis过期删除与内存淘汰策略详解
后端·面试·架构
绝无仅有1 天前
Redis大Key问题排查与解决方案全解析
后端·面试·架构
AAA梅狸猫1 天前
Looper.loop() 循环机制
面试
AAA梅狸猫1 天前
Handler基本概念
面试
Wect1 天前
浏览器缓存机制
前端·面试·浏览器
掘金安东尼1 天前
Fun with TypeScript Generics:玩转 TS 泛型
前端·javascript·面试
掘金安东尼1 天前
Next.js 企业级落地
前端·javascript·面试