news 2026/7/17 12:09:24

JAVA面试之MQ

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JAVA面试之MQ

文章目录

  • RabbitMQ在什么场景下使用?有什么好处?
  • 如何保证消息的可靠传输?如果消息丢了怎么办
    • 生产者丢失
    • MQ中丢失
    • 消费者丢失
    • 兜底方案
  • 失败重试机制
  • 业务幂等性
    • 如何在 RabbitMQ中实现消息幂等性?
  • 如何保证消息的顺序性
  • RabbitMQ的交换机有哪些?
  • 什么是死信队列?死信队列是如何导致的?
    • 什么是死信队列
    • 死信队列是如何导致的
  • RabbitMQ怎么实现延迟队列?
  • ActiveMQ、RabbitMQ、RocketMQ、Kafka对比
    • 1.消息模型与路由
    • 2. 事务消息与分布式事务
      • RocketMQ 的事务消息原理,是分布式理论的绝佳实践
    • 3.顺序消息
    • 4.延时与死信队列
    • 选型指南(面试总结)
    • Kafka 的“百万级吞吐”到底强在哪?
    • 拉模式 vs 推模式
    • 一个你能够主动引导的“选型反例”

RabbitMQ在什么场景下使用?有什么好处?

(1)系统解耦:使用RabbitMQ作为消息的中间传递者,不同的系统之间通过发送和接收消息来进行通信,而不需要直接依赖于彼此。
(2)异步处理:RabbitMQ可以用于异步处理任务。生产者将任务消息发送到RabbitMQ,然后消费者从队列中获取消息并处理。
(3)负载均衡:当有多个消费者订阅同一个队列时,RabbitMQ会将消息均匀地发送给每个消费者,从而实现负载均衡。
(4)优先级队列:RabbitMQ可以为不同的消息设置不同的优先级。
(5)发布/订阅模式:RabbitMQ支持发布/订阅模式,可以同时将消息发送给多个消费者。生产者发布消息到交换机上,多个消费者可以订阅该交换机的队列来接收消息。
(6)持久化和可靠性传输:消息可以被持久化到磁盘上,即使RabbitMQ服务器出现故障,消息也不会丢失。

如何保证消息的可靠传输?如果消息丢了怎么办

数据的丢失问题,可能出现在生产者、MQ、消费者中。
(1)生产者发送消息时丢失:
①生产者发送消息时连接MQ失败
②生产者发送消息到达MQ后未找到Exchange(交换机)
③生产者发送消息到达MQ的Exchange后,未找到合适的Queue(队列)
④消息到达MQ后,处理消息的进程发生异常
(2)MQ导致消息丢失:
消息到达MQ,保存到队列后,尚未消费就突然宕机
(3)消费者处理消息时:
①消息接收后尚未处理突然宕机
②消息接收后处理过程中抛出异常

生产者丢失

(1)可以选择开启 RabbitMQ 提供的事务功能,如果消息没有成功被RabbitMQ 接收到,那么生产者会收到异常报错,此时就可以回滚事务channel.txRollback,然后重新发送消息;如果收到了消息,就可以提交事务channel.txCommit()。但是这样会导致吞吐量下来,因为太耗性能。
(2)另一种方案就是:
a.对于生产者发送消息时连接MQ失败可以开启生产者超时重试机制
b.对于其他情况,比如:生产者发送消息到达MQ后未找到Exchange、生产者发送消息到达MQ的Exchange后,未找到合适的Queue、消息到达MQ后,处理消息的进程发生异常。我们可以使用RabbitMQ提供的Publisher Confirm和Publisher Return两种确认机制。在开启确认机制后,MQ返回给生产者的结果有以下几种情况:
①当消息投递到MQ,但是路由失败时,通过Publisher Return返回异常原因,同时返回ack的确认信息,代表投递成功
②临时消息投递到了MQ,并且入队成功,返回ACK,告知投递成功
③持久消息投递到了MQ,并且入队完成持久化,返回ACK ,告知投递成功
④其它情况都会返回NACK,告知投递失败
其中ack和nack属于Publisher Confirm机制,ack是投递成功;nack是投递失败。而return则属于Publisher Return机制。默认两种机制都是关闭状态,需要通过配置文件来开启。
(3)事务机制和cnofirm机制最大的不同在于,事务机制是同步的,你提交一个事务之后会阻塞在那儿,但是confirm机制是异步的,你发送个消息之后就可以发送下一个消息,然后那个消息RabbitMQ 接收了之后会异步回调你一个接口通知你这个消息接收到了。所以一般在生产者这块避免数据丢失,都是用confirm机制的。
(4)补充(面试问了再回答):publisher-confirm-type有三种模式可选:
①none:关闭confirm机制
②simple:同步阻塞等待MQ的回执
③correlated:MQ异步回调返回回执
一般我们推荐使用correlated,回调机制。

MQ中丢失

为了提升性能,默认情况下MQ的数据都是在内存存储的临时数据,重启后就会消失。为了保证数据的可靠性,必须配置数据持久化,包括:交换机持久化、队列持久化、消息持久化

消费者丢失

(1)消费者确认机制:
为了确认消费者是否成功处理消息,RabbitMQ提供了消费者确认机制(Consumer Acknowledgement)。即:当消费者处理完消息后,应该向RabbitMQ发送一个回执,告知RabbitMQ自己消息处理状态。回执有三种可选值:

  • ack:成功处理消息,RabbitMQ从队列中删除该消息
  • nack:消息处理失败,RabbitMQ需要再次投递消息
  • reject:消息处理失败并拒绝该消息(比如消息格式有问题),RabbitMQ从队列中删除该消息

(2)补充:由于消息回执的处理代码比较统一,因此SpringAMQP帮我们实现了消息确认。并允许我们通过配置文件设置ACK处理方式,有三种模式:

  • none:不处理。即消息投递给消费者后立刻ack,消息会立刻从MQ删除。非常不安全,不建议使用
  • manual:手动模式。需要自己在业务代码中调用api,发送ack或reject,存在业务入侵,但更灵活
  • auto:自动模式。SpringAMQP利用AOP对我们的消息处理逻辑做了环绕增强,当业务正常执行时则自动返回ack. 当业务出现异常时,根据异常判断返回不同结果:
    a.如果是业务异常,会自动返回nack;
    b.如果是消息处理或校验异常,自动返回reject;
    我们选择auto:自动模式即可

兜底方案

(1)虽然我们利用各种机制尽可能增加了消息的可靠性,但也不好说能保证消息100%的可靠。万一真的MQ通知失败该怎么办呢?有没有其它兜底方案,能够确保订单的支付状态一致呢?
(2)其实思想很简单:既然MQ通知不一定发送到交易服务,那么交易服务就必须自己主动去查询支付状态。这样即便支付服务的MQ通知失败,我们依然能通过主动查询来保证订单状态的一致

失败重试机制

(1)当消费者出现异常后,消息会不断requeue(重新入队)到队列,再重新发送给消费者,然后再次异常,再次requeue到队列,无限循环,导致mq的消息处理飙升,带来不必要的压力:
(2)为了应对上述情况Spring又提供了消费者失败重试机制:在消费者出现异常时利用本地重试,而不是无限制的requeue到mq队列。
(3)开启本地重试时,消息处理过程中抛出异常,不会requeue到队列,而是在消费者本地重试;重试达到最大次数后,Spring会返回reject,消息会被丢弃
(4)Spring允许我们自定义重试次数耗尽后的消息处理策略,这个策略是由MessageRecovery接口来定义的,它有3个不同实现:
①RejectAndDontRequeueRecoverer:重试耗尽后,直接reject,丢弃消息。默认就是这种方式
②ImmediateRequeueMessageRecoverer:重试耗尽后,返回nack,消息重新入队
③RepublishMessageRecoverer:重试耗尽后,将失败消息投递到指定的交换机
比较优雅的一种处理方案是RepublishMessageRecoverer,失败后将消息投递到一个指定的,专门存放异常消息的队列,后续由人工集中处理。

业务幂等性

幂等是一个数学概念,用函数表达式来描述是这样的:f(x) = f(f(x)),例如求绝对值函数。
在程序开发中,则是指同一个业务,执行一次或多次对业务状态的影响是一致的。

  • 方案一:给每个消息都设置一个唯一id,利用id区分是否重复消息
    ①每一条消息都生成一个唯一的id,与消息一起投递给消费者
    ②消费者接收到消息后处理自己的业务,业务处理成功后将消息ID保存到数据库
    ③如果下次又收到相同消息,去数据库查询判断是否存在,存在则为重复消息放弃处理
    ④我们该如何给消息添加唯一ID呢?
    其实很简单,SpringAMQP的MessageConverter自带了MessageID的功能,我们只要开启这个功能即可

  • 方案二:业务判断
    业务判断就是基于业务本身的逻辑或状态来判断是否是重复的请求或消息,不同的业务场景判断的思路也不一样。例如我们当前案例中,处理消息的业务逻辑是把订单状态从未支付修改为已支付。因此我们就可以在执行业务时判断订单状态是否是未支付,如果不是则证明订单已经被处理过,无需重复处理。
    相比较而言,消息ID的方案需要改造原有的数据库,所以我更推荐使用业务判断的方案。

如何在 RabbitMQ中实现消息幂等性?

如何保证消息的顺序性

(1)先看看顺序会错乱的场景:RabbitMQ:一个 queue,多个 consumer,这不明显乱了;

(2)解决

RabbitMQ的交换机有哪些?

(1)Direct Exchange:直连交换机,根据Routing Key(路由键)进行投递到不同队列。
(2)Fanout Exchange:扇形交换机,采用广播模式,根据绑定的交换机,路由到与之对应的所有队列。
(3)Topic Exchange:主题交换机,对路由键进行模式匹配后进行投递,符号#表示一个或多个词,*表示一个词。
(4)Header Exchange:头交换机,不处理路由键。而是根据发送的消息内容中的headers属性进行匹配。

什么是死信队列?死信队列是如何导致的?

什么是死信队列

(1)死信队列本身也是一个普通的消息队列,可以通过设置一些参数将其设置为死信队列
(2)无法被消费的消息称为死信,当一个消息在队列中变成死信之后,它能被重新发送到另一个交换机
中,即死信交换机,绑定到死信交换机的队列就称为死信队列。

死信队列是如何导致的

(1)消息过期:
当一个消息过期后,它就会被发送到死信队列。这通常是由于消息的TTL(Time To Live)过期导致的。
(2)消息被拒绝:
当一个消费者拒绝处理某个消息时,这个消息就会被发送到死信队列。这通常是由于消息格式不正确等原因导致的。
(3)队列满了:
当一个队列已经满了,新的消息就无法进入该队列。这时,新的消息就会被发送到死信队列。

RabbitMQ怎么实现延迟队列?

ActiveMQ、RabbitMQ、RocketMQ、Kafka对比

对比维度ActiveMQRabbitMQRocketMQKafka
开发语言JavaErlangJavaScala/Java
单机吞吐量万级万级(几万)十万级百万级
时效性毫秒级微秒级毫秒级毫秒级
消息可靠性高 (Master/Slave)高 (Mirror Queue)极高(同步刷盘/主从)极高 (ISR复制)
功能完备性较完备极强(Exchange路由)强 (事务/延迟/死信)偏弱 (聚焦流处理)
社区生态老牌,稳定庞大,插件丰富阿里主导,国内活跃极强,大数据生态核心
运维难度低到中中 (组件多)中到高 (对硬件有要求)
管理界面一般优秀(Web管理端)较好 (Dashboard)一般 (需外部工具)

1.消息模型与路由

  • ActiveMQ/RabbitMQ:都是“智能代理”模型,支持复杂的路由规则。RabbitMQ 的 Exchange 能实现几乎任意路由组合。
  • RocketMQ/Kafka:都是“傻代理”模型,路由规则简单,只发布到 Topic。区别在于RocketMQ 支持 Tag 和 SQL92 过滤,在 Topic 之下再做一层子分类,比 Kafka 灵活。
    备注:

1.“所谓任意路由组合,是指 RabbitMQ 提供了 Direct、Fanout、Topic、Headers 四种交换机,并且支持把多个交换机和队列通过不同的绑定键(Binding Key)串联,形成复杂的拓扑。
2.Topic 就是消息的主题或类别。生产者把消息发到某个 Topic,消费者订阅这个 Topic 来接收消息。
类比:就像你订阅的微信公众号。公众号是 Topic,发布文章就是发消息;你关注它,就能收到推送。RabbitMQ 的 Exchange 在"消息投递入口"这个角色上,相当于 Kafka/RocketMQ 的 Topic。RabbitMQ 的 Exchange 只负责路由逻辑,不存消息,真正存储消息的是 Queue,这和 Kafka 的 Topic 既是逻辑分类又是物理存储单元,有本质区别。
3.Tag(标签):消息的第二级分类标识。一个 Topic 下可以有多个 Tag,生产者在发消息时指定。
类比:Topic 是"邮件”,Tag 是"邮件标题"。同一个 Topic 的所有消息都在一个快递车里,但消费者可以根据标题决定是否处理。
4… SQL92 过滤:在 Tag 的基础上,支持用 SQL 风格的语法对消息的用户属性(Property)做条件过滤。

2. 事务消息与分布式事务

  • RocketMQ 是唯一商业级支持事务消息的。它的“半消息”机制可以完美解决分布式事务的最终一致性问题。
  • ActiveMQ/RabbitMQ基于 AMQP/JMS 标准,有本地事务支持,但分布式事务场景需要自研本地消息表等方案。
  • Kafka后来加入事务,但主要用于流处理内部的精确一次语义。

RocketMQ 的事务消息原理,是分布式理论的绝佳实践

你提到了它是唯一商业级支持,可以简短用几句话概括它的“半消息”机制,体现你深入理解了:

RocketMQ 的分布式事务,核心是事务反查机制

  1. 生产者先发一条对消费者不可见的“半消息”。
  2. 然后执行本地事务,根据结果向 Broker 提交(Commit)或回滚(Rollback)。
  3. 关键是:如果 Broker 因网络波动长时间没收到确认,它会主动回调生产者接口,反查本地事务的最终状态。这个反查机制,巧妙解决了网络不确定性带来的悬挂问题。

3.顺序消息

(1)所有消息队列都保证分区(队列)内严格有序。
(2)RocketMQ 在这方面做得最完善,通过 MessageQueueSelector 可将业务标识相同的消息(如同一订单ID)发送到同一队列,轻松实现全局部分有序。

  • RocketMQ 的“完美”是有代价的:它通过同一队列(Queue)串行投递来保证严格顺序。但在高并发下,如果某个订单的消息处理阻塞,会卡住整个队列,导致其他订单也无法消费。这需要配合非阻塞消费逻辑和熔断降级。
    (3)Kafka也是把消息按 Key 哈希到分区保证顺序,但扩容分区会破坏顺序,这是架构硬伤
  • Kafka 的“硬伤”其实可以解:你可以补充说,Kafka 的顺序保证单位是分区,扩容确实会破坏 Key 的分区映射。所以在数据架构设计时,通常会对 Key 做预分区(如初始就创建 64 或 128 个分区),短期靠增大单分区吞吐量顶住,长期的数据再平衡则需要上游配合重置 Offset,成本很高。这体现的是架构师的预见性。

4.延时与死信队列

  • RabbitMQ通过插件和 TTL+DLX 组合实现。
  • RocketMQ原生支持定时/延时消息(商业版支持任意延时),且内置死信队列,业务属性最强。

选型指南(面试总结)

  • ActiveMQ慎用,避坑。适合维护遗留系统,新项目别选。
  • RabbitMQ企业应用与微服务解耦。适合对路由要求高、需要毫秒级/微秒级可靠投递的场景(如订单通知、审批流),并且开发运维友好。
  • RocketMQ高并发核心在线业务。如阿里的电商、金融场景,尤其当你有分布式事务全链路顺序等刚性需求时。
  • Kafka大数据和流处理管道。海量日志采集、用户行为埋点、实时计算的数据源,是Hadoop/Flink生态的事实标准。

Kafka 的“百万级吞吐”到底强在哪?

你说到了吞吐量数据,如果再解释一下底层原理,会非常加分:

  • 顺序 I/O:Kafka 将消息以追加方式写入分区日志文件,把磁盘的随机写变成了顺序写,极致压榨了机械硬盘甚至 SATA SSD 的性能。
  • 零拷贝(Zero-Copy):Kafka 消费消息时,通过sendfile系统调用,数据从磁盘缓存直接传输到网卡缓冲区,绕过了用户态内核态的多次数据拷贝,大幅降低 CPU 使用率。这是它比 RocketMQ(基于 Netty 有多次内存拷贝)吞吐量高一个量级的核心原因。

拉模式 vs 推模式

你可以点出它们在消费模型上的本质区别:

  • RabbitMQ 是纯“推”模式:Broker 主动推送,优点是实时性极高,缺点是当消费者处理慢时,容易产生消息堆积导致消费者内存溢出。可以通过设置prefetch做流量控制。
  • RocketMQ 和 Kafka 是“拉”模式:消费者根据自身处理能力主动拉取,天然具备消费削峰能力,更适配高吞吐场景。RocketMQ 为平衡实时性,做了“长轮询”,不至于空转。

一个你能够主动引导的“选型反例”

如果面试官让你说一个反常识的案例,这会让你脱颖而出:

“提到 Kafka 不是只等于大数据。在现代事件驱动架构(EDA)DDD(领域驱动设计)微服务中,Kafka 常被用作事件中心,利用其持久化和重播能力实现事件溯源(Event Sourcing)。这个场景下,Kafka 不是管道,而是事实来源(Source of Truth)。”

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/17 12:09:15

3个简单命令搞定微信聊天记录永久保存:你的数字记忆不该丢失

3个简单命令搞定微信聊天记录永久保存:你的数字记忆不该丢失 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we…

作者头像 李华
网站建设 2026/7/17 12:09:11

4步掌握洛雪音乐音源:从零到精通的全网音乐聚合解决方案

4步掌握洛雪音乐音源:从零到精通的全网音乐聚合解决方案 【免费下载链接】lxmusic- lxmusic(洛雪音乐)全网最新最全音源 项目地址: https://gitcode.com/gh_mirrors/lx/lxmusic- 还在为不同音乐平台的版权壁垒而烦恼吗?洛雪音乐音源项目通过聚合Q…

作者头像 李华
网站建设 2026/7/17 12:07:37

Bilibili-Old终极指南:三步找回你的B站经典界面,告别复杂新设计

Bilibili-Old终极指南:三步找回你的B站经典界面,告别复杂新设计 【免费下载链接】Bilibili-Old 恢复旧版Bilibili页面,为了那些念旧的人。 项目地址: https://gitcode.com/gh_mirrors/bi/Bilibili-Old 你是否还在怀念B站那个简洁清爽的…

作者头像 李华
网站建设 2026/7/17 12:06:10

如何快速掌握yuzu模拟器:从安装到精通Switch游戏的完整指南

如何快速掌握yuzu模拟器:从安装到精通Switch游戏的完整指南 【免费下载链接】yuzu 任天堂 Switch 模拟器 项目地址: https://gitcode.com/GitHub_Trending/yu/yuzu 想象一下,在电脑上流畅运行《塞尔达传说:旷野之息》这样的Switch独占…

作者头像 李华
网站建设 2026/7/17 12:05:32

江西省抚州市临川区学墅壹号五层别墅电梯落地:邻居+设计师双重转介绍,香槟金封闭式钢构家用电梯定制交付

在江西省抚州市临川区,楼梯中间加装是别墅家用电梯的主流方案,不少业主为控制装修预算,会选择自主采购材料、安排施工队伍搭建钢结构井道,但缺乏专业技术指导时,容易出现焊接不规范、垂直度偏差、空间规划不合理等问题…

作者头像 李华