MQ 面试
MQ 面试
MQ 简介
【简单】MQ 是什么?🌟
MQ(Message Queue,消息队列) 是一种异步通信机制,用于在不同服务、应用或系统组件之间可靠地传递消息。它的核心思想是解耦生产者和消费者,通过缓冲消息来提高系统的可靠性、扩展性和可维护性。
MQ 的核心概念
- 生产者(Producer):发送消息的应用或服务。
- 消费者(Consumer):接收并处理消息的应用或服务。
- 消息(Message):传输的数据单位,可以是文本、JSON、二进制等格式。
- 队列(Queue):存储消息的缓冲区,遵循 FIFO(先进先出) 或优先级策略。
- Broker(消息代理):负责接收、存储和转发消息的中间件(如 RabbitMQ、Kafka)。
- 背压:背压是一种消息传递机制中的流控策略。当消费者处理速度跟不上生产者发送速度时,通过反向施加压力,迫使生产者降速或停止发送,以防止系统被压垮。
目前主流的 MQ 有:Kafka、RabbitMQ、RocketMQ、ActiveMQ。
【简单】MQ 有哪些应用场景?🌟🌟
关键点
MQ 的典型应用场景
- 异步处理
- 系统解耦
- 流量削峰
- 系统间通信
- 传输缓冲
- 最终一致性
异步处理
MQ 可以将系统间的处理流程异步化,减少等待响应的时间,从而提高整体并发吞吐量。一般,MQ 异步处理应用于非核心流程,例如:短信/邮件通知、数据推送、上报数据到监控中心、日志中心等。
假设这样一个场景,用户向系统 A 发起请求,系统 A 处理计算只需要 10ms
,然后通知系统 BCD 写库,系统 BCD 写库耗时分别为:100ms
、200ms
、300ms
。最终总耗时为:10ms+100ms+200ms+300ms=610ms
。此外,加上请求和响应的网络传输时间,从用户角度看,可能要等待将近 1s
才能得到结果。

如果使用 MQ,系统 A 接到请求后,耗时 10ms
处理计算,然后向系统 BCD 连续发送消息,假设耗时 5ms
。那么 这一过程的总耗时为 3ms + 5ms = 8ms
,这相比于 610 ms
,大大缩短了响应时间。至于系统 BCD 的写库操作,只要自行消费 MQ 后处理即可,用户无需关注。

系统解耦
通过 MQ,可以消除系统间的强耦合。它的好处在于:
- 消息的消费者系统可以随意增加,无需修改生产者系统的代码。
- 生产者系统、消费者系统彼此不会影响对方的流程。
- 如果生产者系统宕机,消费者系统收不到消息,就不会有下一步的动作。
- 如果消费者系统宕机,生产者系统让然可以正常发送消息,不影响流程。
不同系统如果要建立通信,传统的做法是:调用接口。
如果需要和新的系统建立通信或删除已建立的通信,都需要修改代码,这种方案显然耦合度很高。

如果使用 MQ,系统间的通信只需要通过发布/订阅(Pub/Sub)模型即可,彼此没有直接联系,也就不需要相互感知,从而达到 解耦。

流量削峰
当 上下游系统 处理能力存在差距的时候,利用 MQ 做一个 “漏斗” 模型,进行 流控。把 MQ 当成可靠的 消息缓冲池,进行一定程度的 消息堆积;在下游有能力处理的时候,再发送消息。
MQ 的流量削峰常用于高并发场景(例如:秒杀、团抢等业务场景),它是缓解瞬时暴增流量的核心手段之一。
如果没有 MQ,两个系统之间通过 协商、滑动窗口、限流/降级/熔断 等复杂的方案也能实现 流控。但 系统复杂性 指数级增长,势必在上游或者下游做存储,并且要处理 定时、拥塞 等一系列问题。而且每当有 处理能力有差距 的时候,都需要 单独 开发一套逻辑来维护这套逻辑。
假设某个系统读写数据库的稳定性能为每秒处理 1000 条数据。平常情况下,远远达不到这么大的处理量。假设,因为因为做活动,系统的瞬时请求量剧增,达到每秒 10000 个并发请求,数据库根本承受不了,可能直接就把数据库给整崩溃了,这样系统服务就不可用了。

如果使用 MQ,每秒写入 10000 条请求,但是系统 A 每秒只从 MQ 中消费 1000 条请求,然后写入数据库。这样,就不会超过数据库的承受能力,而是把请求积压在 MQ 中。只要高峰期一过,系统 A 就会很快把积压的消息给处理掉。

系统间通信
消息队列一般都内置了 高效的通信机制,因此也可以用于单纯的 消息通讯,比如实现 点对点消息队列 或者 聊天室 等。
生产者/消费者 模式,只需要关心消息是否 送达队列,至于谁希望订阅和需要消费,是 下游 的事情,无疑极大地减少了开发和联调的工作量。
传输缓冲
(1)MQ 常被用于做海量数据的传输缓冲。
例如,Kafka 常被用于做为各种日志数据、采集数据的数据中转。然后,Kafka 将数据转发给 Logstash、Elasticsearch 中,然后基于 Elasticsearch 来做日志中心,提供检索、聚合、分析日志的能力。开发者可以通过 Kibana 集成 Elasticsearch 数据进行可视化展示,或自行进行定制化开发。

(2)MQ 也可以被用于流式处理。
例如,Kafka 几乎已经是流计算的数据采集端的标准组件。而流计算通过实时数据处理能力,提供了更为快捷的聚合计算能力,被大量应用于链路监控、实时监控、实时数仓、实时大屏、风控、推荐等应用领域。
最终一致性
最终一致性 不是 消息队列 的必备特性,但确实可以依靠 消息队列 来做 最终一致性 的事情。
- 先写消息再操作,确保操作完成后再修改消息状态。定时任务补偿机制 实现消息 可靠发送接收、业务操作的可靠执行,要注意 消息重复 与 幂等设计。
- 所有不保证
100%
不丢消息 的消息队列,理论上无法实现 最终一致性。
像
Kafka
一类的设计,在设计层面上就有 丢消息 的可能(比如 定时刷盘,如果掉电就会丢消息)。哪怕只丢千分之一的消息,业务也必须用其他的手段来保证结果正确。
【中等】MQ 存在哪些挑战?🌟
任何技术都会有利有弊,MQ 给整体系统架构带来很多好处,但也会付出一定的代价。
MQ 主要引入了以下问题:
- 系统可用性降低:引入了 MQ 后,通信需要基于 MQ 完成,如果 MQ 宕机,则服务不可用。因此,MQ 要保证是高可用的。
- 系统复杂度提高:使用 MQ,需要关注一些新的问题:
- 如何保证消息没有 重复消费?
- 如何处理 消息丢失 的问题?
- 如何保证传递 消息的顺序性?
- 如何处理大量 消息积压 的问题?
- 一致性问题:假设系统 A 处理完直接返回成功的结果给用户,用户认为请求成功。但如果此时,系统 BCD 中只要有任意一个写库失败,那么数据就不一致了。这种情况如何处理?
【中等】Kafka、RocketMq、RabbitMQ 如何选型?🌟
特点与适用场景对比
类别 | Kafka | RocketMQ | RabbitMQ |
---|---|---|---|
核心优势 | 超高吞吐量 极佳的海量数据堆积能力 强大的生态集成 | 高吞吐与低延迟兼备 金融级事务消息 阿里系生产环境考验 | 灵活的路由功能 丰富的企业级特性 强大的管理界面与多协议支持 |
主要劣势 | 功能相对单一,路由能力弱 对于简单业务,部署和运维相对复杂 | 社区和生态相对于 Kafka 较小 命名与 Kafka 有差异,学习有成本 | 吞吐量和堆积能力远低于前两者 性能受内存和磁盘碎片影响更大 |
典型适用场景 | 日志收集与聚合 流式处理与实时数据管道 事件溯源 大数据领域 | 电商、金融等核心交易系统 高并发订单处理 对事务一致性要求高的场景 | 企业级应用集成(复杂路由) 任务分发、RPC 对吞吐要求不极高的业务解耦 |
不适用场景 | 需要复杂路由、低延迟(毫秒级)响应的业务 | 需要与 Kafka 生态紧密集成的项目 | 需要海量消息堆积和高吞吐量的场景 |
MQ 存储
【中等】Kafka、RocketMq、RabbitMQ 如何存储数据?🌟🌟
逻辑与物理存储模型对比
特性 | Kafka | RocketMQ | RabbitMQ |
---|---|---|---|
核心逻辑模型 | 发布/订阅的日志流 | 发布/订阅的消息队列 | 队列与交换器的路由代理 |
逻辑存储 | 主题(Topic) 分区(Partition) 记录(Record) | 主题(Topic) 队列(MessageQueue) 消息(Message) | 交换机(Exchange) 队列(Queue) |
物理存储 | Log(对应 Partition) LogSegment | 提交日志(CommitLog) 消费队列(ConsumeQueue) | Queue 对应的消息存储文件 |
存储文件 | .log (数据文件).index (偏移量索引).timeindex (时间索引) | CommitLog (提交日志)ConsumeQueue (消费队列)IndexFile (哈希索引) | .rdq (消息体文件).idx (消息索引与元数据文件) |
持久化方式 | 追加写入 + 零拷贝 | 追加写入 + 内存映射 | 可变长度写入,支持内存/磁盘持久化 |
刷盘方式 | 数据先写入页缓存,再在合适时机写入磁盘 | 数据先写入页缓存,再在合适时机写入磁盘 | |
数据清理 | 基于时间或空间的保留策略 | 基于时间或空间的保留策略 | 消费确认后立即删除 |
消费组模型 | 消费者组(Consumer Group) | 消费者组(Consumer Group) | 无明确组概念,客户端竞争消费同一队列 |
消息获取模式 | 消费者按 Offset 主动拉取 | 消费者主要按 Offset 主动拉取 | Broker 主动推送 给消费者(主流),也支持拉取 |
【中等】Kafka、RocketMq、RabbitMQ 如何持久化?🌟🌟
核心要点对比
机制 | Kafka | RocketMQ | RabbitMQ |
---|---|---|---|
核心设计 | 日志即存储 | 读写分离 | 队列即存储 |
写入方式 | 顺序追加写入 | 顺序追加写入 (CommitLog) | 可变长度写入 |
刷盘机制 | 异步(高性能) 同步(高可靠) | 异步 (高性能) 同步 (高可靠) | 异步 确认后删除 |
数据清理 | 基于保留策略 时间 大小 压缩 (Key 去重) | 基于保留策略 时间 大小 | 消费后删除 确认后立即删除 队列长度限制 |
索引机制 | 稀疏索引.index (偏移量).timeindex (时间戳) | 多级索引ConsumeQueue (核心逻辑索引)IndexFile (关键字查询) | 队列索引 消息存储索引 内存状态记录(性能瓶颈) |
总结
- Kafka:为超高吞吐和海量数据堆积设计,采用最极致的顺序 I/O,像一个只增不删的日志系统。
- RocketMQ:在 Kafka 的基础上做了优化,通过读写分离的设计,在保证高吞吐写入的同时,兼顾了低延迟读取和事务消息等金融级需求。
- RabbitMQ:核心在于灵活的路由和可靠交付,其存储设计不适合海量消息堆积,但能实现复杂的消息路由和“确认即删除”的精准控制。
MQ 生产消费
【中等】MQ 数据传输有哪些模式?🌟🌟
MQ 数据传输的模式主要分为 Push(推) 和 Pull(拉) 两种,不同消息队列中间件采用不同的策略,部分系统还支持 混合模式。

消息队列消费模式对比:
维度 | Push | Pull | 长轮询 | 混合模式 |
---|---|---|---|---|
特点 | Broker 主动推送给消费者,消费者被动接收;实时性高,减少消费者轮询开销 | 消费者主动从 Broker 拉取消息,按需获取;消费者控制消费速率 | Push 和 Pull 的折中方案;请求被保持,直到有消息或超时 | 关键消息用 Push,批量数据用 Pull;消费者可动态切换模式 |
优点 | 低延迟,适合实时性要求高的场景(如即时通讯) | 避免消息堆积冲击消费者,适合高吞吐场景(如日志处理) | 减少无效轮询,平衡实时性与服务端压力 | 灵活性强,能兼顾实时性和吞吐量,适应复杂场景 |
缺点 | 可能造成消费者过载(需背压机制控制流速) | 存在空轮询开销(可通过长轮询优化) | 实现复杂度高于纯 Pull,需维护挂起的请求连接 | 系统设计和实现的复杂度最高 |
典型实现 | RabbitMQ、ActiveMQ、RocketMQ(默认长轮询模拟 Push) | Kafka、Pulsar(原生 Pull)、RocketMQ(支持显式 Pull) | RocketMQ(默认模式)、HTTP 长轮询(如 WebSocket) | Pulsar(支持多模式)、部分自研 MQ 系统 |
选择建议
- 需要低延迟 → Push(如 RabbitMQ)
- 需要高吞吐 → Pull(如 Kafka)
- 平衡场景 → 长轮询(如 RocketMQ)
【中等】MQ 有哪些通信模型?🌟
消息队列(MQ)的两种核心通信模型:
- 点对点模型
- 核心特点:消息存储在队列中。一条消息只能被一个消费者成功消费和处理,存在消费竞争关系。
- 应用场景:适用于任务分发和工作队列,例如视频转码、报表生成等需要并行处理任务的场景。
- 发布/订阅模型
- 核心特点:消息被发布到主题中。一条消息会被广播给所有订阅了该主题的消费者,消息被所有订阅者共享,没有竞争关系。
- 应用场景:适用于事件广播,例如一个“订单创建”事件需要被库存、营销等多个系统同时感知和处理。
现代 MQ(如 Kafka)的模型融合:
- 通过消费者组的概念融合了两种模型。
- 不同消费者组之间是发布/订阅模式(每个组都能收到全量消息)。
- 同一消费者组内部是点对点模式(组内只有一个消费者能处理某条消息)。
【困难】Kafka、RocketMq、RabbitMQ 生产消费有什么相同和不同之处?🌟🌟🌟
Kafka、RocketMq、RabbitMQ 生产消费的相同点:
- 生产消息都支持同步、异步发送。
- 都支持消息压缩以节省带宽/存储。
- 都支持多种序列化方式。
- 都通过分区机制来支持并发与扩展。
Kafka、RocketMq、RabbitMQ 生产消费的不同点:
特性维度 | Kafka | RocketMQ | RabbitMQ |
---|---|---|---|
生产/消费模型 | 基于日志的发布/订阅 | 基于队列的发布/订阅 | 灵活的路由 (Exchange -> Queue) |
同步/异步发送 | 同步:send().get() 异步: send(callback) 单向: send() 无回调(不关心结果) | 同步:send() 阻塞异步: sendAsync(callback) 单向: sendOneway() | 同步:使用 Channel 的确认机制异步:使用 Confirm Mode 和回调 |
推拉模型 | 消费者主动拉取 | 消费者主动拉取 | Broker 主动推送 (默认) (也支持拉取 Get ,但不常用) |
是否支持批量生产 | 是,核心功能 生产者会累积消息,批量发送到同一 Partition,极大提升吞吐 | 是,核心功能 支持将多条消息打包成一个批次发送 | 是,但较弱 通过 Publisher Confirms 和 Tx 模拟,或使用第三方插件。原生对批量不友好 |
是否支持批量消费 | 是,核心功能 消费者一次拉取一批消息进行处理。 | 是,核心功能 消费者可以设置一次拉取的消息条数 | 是,通过 Qos 通过设置 prefetchCount 来控制未确认消息的批量大小,实现“准批量” |
序列化方式 | 高度自由,与协议解耦 生产者/消费者自行配置序列化器(如 String, JSON, Avro, Protobuf) | 高度自由,与协议解耦 同上,客户端可灵活配置 | 与协议绑定 依赖于 AMQP 等协议规定的格式,灵活性较低。消息体为二进制,具体格式由应用层决定 |
是否支持压缩 | 是,端到端压缩 支持 Snappy, Gzip, LZ4, Zstandard。生产者压缩,消费者解压,节省带宽和存储。 | 是,端到端压缩 支持 Gzip, Snappy, LZ4, Zstandard。机制与 Kafka 类似。 | 是,传输层压缩 通常指在传输协议层面(如 TLS)的压缩,而非消息内容的端到端压缩。 |
如何支持并发 | 基于 Partition 一个 Consumer Group 内的多个消费者并行消费同一个 Topic 的不同 Partition 单 Partition 内消息有序,Partition 间无序 | 基于 Queue 一个 Consumer Group 内的多个消费者并行消费同一个 Topic 的不同 Queue 单 Queue 内消息有序,Queue 间无序 | 基于 Queue 多个消费者绑定到同一个 Queue 时,消息以竞争方式被消费(如 Round-Robin) 通过多个 Queue 来实现并发 |
如何扩展 | 水平扩展:增加 Partition 通过增加 Topic 的 Partition 数量来提升并发度 消费者数量最好 ≤ Partition 数量 | 水平扩展:增加 Queue 通过增加 Topic 的 Queue 数量来提升并发度 消费者数量最好 ≤ Queue 数量 | 水平扩展:增加 Queue/Node 纵向:为一个 Exchange 绑定多个 Queue 横向:搭建集群,使用 镜像队列 实现高可用 |
核心差异总结
- 推拉模型差异:
- Kafka & RocketMQ:拉模式。消费者按需拉取消息,可以方便地实现批处理和流量控制。
- RabbitMQ:推模式。Broker 主动将消息推送给消费者,延迟更低,消费者需要有背压机制。
- 并发与有序性:
- 三者都通过 分区机制(Partition/Queue) 来实现并发。
- 它们都通过在 分区内部保证顺序 来兼顾并发与局部有序。若需要全局有序,就必须只有一个分片和一个消费者,这会牺牲并发性。
- 批处理:
- Kafka & RocketMQ:批处理是核心设计理念,从生产到消费都深度优化,是追求极致吞吐量的关键。
- RabbitMQ:设计核心是复杂的路由和可靠的单条消息传递,批量处理是其短板,通常通过 prefetch 来模拟。
- 扩展性:
- Kafka & RocketMQ:扩展性直接与主题的分片数挂钩,规划初期需要合理设置。
- RabbitMQ:扩展性更灵活,可以通过增加队列或搭建集群来实现,但其队列镜像对性能有一定影响。
MQ 集群
【困难】Kafka、RocketMq、RabbitMQ 副本机制有什么不同之处?🌟🌟🌟
副本机制对比
特性维度 | Kafka | RocketMQ | RabbitMQ |
---|---|---|---|
核心模型 | 主从复制 | 主从复制 | 镜像队列 (Mirrored Queue) |
副本单位 | Partition (分区) | Broker 组 (Broker Group) | Queue (队列) |
数据一致性 | ISR (同步副本集合) 机制 Leader 维护一个与其保持同步的 Follower 列表(ISR) 消息只有被 ISR 中的所有副本 应用后才能被消费者读取 提供强一致性保证 | 多数派写入 + 主从同步 同步双写 (Dledger):基于 Raft 协议,消息写入多数节点后才确认,强一致 异步复制:消息在主节点落盘即返回,数据有丢失风险,最终一致 | 镜像队列同步 所有发布到主队列的消息都会同步到所有镜像 支持配置同步镜像(强一致,性能低)和异步镜像(最终一致,性能高) |
读写流量 | 主副本读写 所有读写请求只由 Leader 副本处理 Follower 副本只负责从 Leader 异步拉取数据进行同步 | 主副本读写 所有读写请求只由 Master 节点处理 Slave 节点作为热备,只同步数据,不提供服务 | 主队列读写 客户端可与集群中任何节点通信 但针对某个队列的读写请求最终都会被路由到该队列的主节点上 |
故障转移 | 自动选举 Controller 从 ISR 集合 中自动选举新的 Leader 服务不可用时间短,自动化程度高 | 自动切换 Dledger 模式:基于 Raft 协议自动选举新 Master 主从模式:依赖 NameServer 和监控脚本进行主从切换,可能需人工介入 | 自动提升 当主队列所在节点宕机时,最老的镜像会被自动提升为新的主队列 客户端需要重连才能感知到新的主节点 |
优点 | 高吞吐:主写主读,压力集中 强一致性:ISR 机制保证数据不丢失 自动化:故障转移自动完成 | 灵活可选:提供强一致和最终一致两种模式 金融级可靠:Dledger 模式保证数据强一致和高可用 热备份:Slave 节点数据完整,切换快 | 使用简单:对客户端透明,连接任何节点即可 灵活配置:可针对不同队列设置不同的镜像策略 高可用:队列数据在多个节点冗余 |
缺点 | 资源利用率低:Follower 副本只做备份,不服务读写 ISR 抖动:Follower 同步慢会被踢出 ISR,影响可用性 | 主从模式非强一致:异步复制有数据丢失风险 主从切换可能延迟:非 Dledger 模式下,自动化程度不如 Kafka | 性能瓶颈:同步镜像模式下,性能受限于最慢的镜像节点 网络压力大:所有写操作都需要在镜像间同步,网络 IO 高 扩展性限制:队列的镜像数量越多,性能和网络压力越大 |
核心差异总结
设计哲学与一致性
- Kafka:以 ISR 机制为核心,在保证高吞吐的同时,提供了强一致性的折中方案。它不要求所有副本都写入成功,只要求一个动态的、健康的副本集合(ISR)同步即可。
- RocketMQ:提供了 灵活的选择。在金融场景下,可以使用基于 Raft 的 Dledger 模式获得强一致性;在普通场景下,可以使用异步复制获得更高吞吐。
- RabbitMQ:以 镜像队列 为单位进行复制,其一致性级别(强一致或最终一致)取决于镜像配置,更偏向于最终一致性和使用便利性。
读写模式
- Kafka & RocketMQ:都是 "主写主读" 。所有流量都集中在主副本,架构简单,延迟可控。
- RabbitMQ:是 "主写主读,但客户端可连任意节点" 。对客户端更友好,但最终压力还是在主节点上。
故障转移
- Kafka:自动化程度最高,从预设的 ISR 中快速自动选举。
- RocketMQ (Dledger):自动化且强一致,基于 Raft 协议自动选举。
- RocketMQ (主从) & RabbitMQ:自动化但有延迟或依赖。RocketMQ 主从可能需监控脚本;RabbitMQ 客户端需要重连来感知新主节点。
【困难】Kafka、RocketMq、RabbitMQ 分区机制有什么不同之处?🌟🌟🌟
核心分区/分片机制对比
特性维度 | Kafka | RocketMQ | RabbitMQ |
---|---|---|---|
核心概念 | 分区 | 队列 | 队列 |
逻辑实体 | Topic | Topic | Exchange + Queue |
消息有序性 | 保证 Partition 内有序,Partition 间无序 | 保证 Queue 内有序,Queue 间无序 | 保证 Queue 内有序。如果多个消费者消费同一队列,顺序无法保证 |
消息路由方式 | 指定 Key:相同 Key 的消息被哈希到同一 Partition 轮询:保证分区间负载均衡 随机/自定义 | 轮询:默认方式,保证队列间负载均衡 哈希:通过 Sharding Key 选择队列 消息队列选择器:自定义算法 | Exchange 类型 和 Routing Key 决定: Direct:精确匹配 Routing Key Topic:模糊匹配模式 Fanout:广播到所有绑定队列 Headers:匹配消息头 |
与消费者的关系 | 一个 Consumer Group 内,一个 Partition 只能被一个 Consumer 独占 | 一个 Consumer Group 内,一个 Queue 只能被一个 Consumer 独占 | 一个 Queue 可以被多个 Consumer 共享竞争。消息以轮询方式分发给这些消费者 |
扩展性 | 通过增加 Topic 的 Partition 数量来实现水平扩展 注意:增加 Partition 数量需要重启或使用工具,操作较重 | 通过增加 Topic 的 Queue 数量来实现水平扩展 注意:Queue 数量在创建后通常不建议修改 | 通过增加 Queue 的数量并与 Exchange 绑定来实现扩展,操作相对灵活 |
设计哲学 | "日志流" 分片:将巨大的 Topic 日志流切分成多个 Partition,分散存储和压力 | "消息队列" 分片:概念上更接近传统的队列,将一个 Topic 的消息负载均衡到多个队列 | "消息路由":核心在于 Exchange 如何将消息灵活地路由到不同的队列,队列是消息的终点 |
小结
- Kafka & RocketMQ:
- 通过 分区/队列 进行分片,实现并行。
- 通过 分区/队列内独占消费 来保证消息顺序。
- 提升并发必须增加分片。
- RabbitMQ:
- 通过 Exchange 路由 将消息分发到不同队列。
- 通过 队列内共享消费 来提升并发,但会牺牲消息顺序。
- 路由方式极其灵活。
MQ 可靠传输
【困难】如何保证 MQ 消息不丢失?🌟🌟🌟
要保证 MQ 中的消息不丢失,需从 生产端、MQ 服务端、消费端 三个环节进行可靠性设计。一言以蔽之,生产端确认+服务端持久化+消费端手动 ACK+监控补偿 是保证消息不丢失的核心逻辑,需根据业务场景权衡性能与可靠性。
生产端防丢失
Kafka | RocketMQ | |
---|---|---|
发送方式 | 支持同步、异步、异步回调发送方式 同步性能低;异步无视成功与否;异步回调比较合适 | 支持同步、异步回调发送方式 同步性能低;异步回调比较合适 |
生产 ACK | acks 参数确保消息被多少副本写入成功后才返回确认min.insync.replicas 配合 acks 使用,设定最少写入副本数 | (配合刷盘机制)若配置为同步刷盘,消息必须先成功写入磁盘,发送方才能收到写成功的确认 |
失败重试 | retries 参数控制失败重试次数 | retryTimesWhenSendFailed 参数控制失败重试次数 |
事务 | 通过事务+生产幂等,实现 exactly once 语义,非真正的分布式事务 | 可实现分布式事务 |
MQ 服务端防丢失
Kafka | RocketMQ | |
---|---|---|
持久化 | 消息写入页缓存(PageCache),即可返回生产 ACK,由 OS 负责刷盘(fsync )可设置自动刷盘的间隔时间或消息数阈值 可以通过 AdminClient 手动刷盘 | 默认采用异步刷盘,类似 Kafka,先写入页缓存(PageCache),由 OS 负责刷盘(fsync )也支持同步刷盘,消息必须先成功写入磁盘,发送方才能收到写成功的确认 |
副本机制 | 通过副本机制保证冗余,避免单点故障 副本的粒度是针对分区 replication.factor 设置分区副本数min.insync.replicas 控制写入到多少个副本才算是“已提交” | 通过副本机制保证冗余,避免单点故障 副本的粒度是针对节点(Broker) |
故障检测 | 所有 Broker 会向 zk 的/borker 路径写临时节点,而 Controller 会监听该目录一旦有 Broker 故障或失联,zk 会话中断,zk 自动删除临时节点,并通知 Controller Controller 将 Broker 视为下线,并将该 Broker 上的分区 Leader 置为无效 | 主节点定期向从节点发送心跳以续活 超时未收到消息,从节点视主节点为下线 |
故障恢复 | Controller 负责选新的分区 Leader,优先从 ISR 中选 通过 unclean.leader.election.enable 可设置是否允许从非 ISR 中选 Leader,但丢失数据风险增大 | 采用 Raft 来选主 |
消费端防丢失
Kafka | RocketMQ | |
---|---|---|
消费 ACK | 支持自动、手动提交 Offset(enable.auto.commit=false 配置)关闭自动提交,处理完整事务后,再手动提交 Offset | 支持自动、手动提交 Offset(consumer.setAutoCommit 配置) 关闭自动提交,处理完整事务后,再手动提交 Offset |
重置 Offset | auto.offset.reset 可以重置消费的 Offset | |
失败重试 | 不完善:自动提交不支持重试;手动提交续自行处理重试逻辑 | 默认重试 16 次 多次消费失败后会被投递到死信队列,供后续特殊处理 |
监控与补偿
- 消息堆积告警:监控队列长度,及时发现异常。
- 定期对账:对比生产与消费的记录,修复差异(如定时扫描数据库补发)。
【困难】如何保证 MQ 消息不重复?🌟🌟🌟
MQ 为什么会出现重复消息?
- 生产端重试(网络抖动时自动重发)
- 消费端超时后 MQ 重新投递(如 RabbitMQ 未及时 ACK)
- 消息队列集群脑裂(如 Kafka 副本切换)

以 Kafka 举例,Kafka 每个 Partition 都是一个有序的、不可变的记录序列,不断追加到结构化的提交日志中。Partition 中为每条记录分配一个连续的 id 号,称为偏移量(Offset),用于唯一标识 Partition 内的记录。
Kafka 的客户端和 Broker 都会保存 Offset。客户端消费消息后,每隔一段时间,就把已消费的 Offset 提交给 Kafka Broker,表示已消费。
在这个过程中,如果客户端应用消费消息后,因为宕机、重启等情况而没有提交已消费的 Offset 。当系统恢复后,会继续消费消息,由于 Offset 未提交,就会出现重复消费的问题。
重复消息通用解决方案
处理重复消息 = “业务幂等为基础,缓存/DB 去重为辅助,监控兜底保万一”。
处理 MQ 重复消息的核心思路是 幂等性设计 + 去重机制,确保即使消息被多次消费,业务结果也不会出错。
(1)业务层幂等设计
唯一标识:每条消息携带唯一业务 ID(如订单号、支付流水号),处理前先查库判断是否已执行。
SELECT status FROM orders WHERE order_id = '123'; -- 若已处理则直接跳过
状态机控制:业务状态严格流转(如「已支付」订单不允许重复扣款)。
(2)去重表/缓存
数据库去重表:消费前先
INSERT
唯一键(消息 ID),利用主键冲突避免重复处理。INSERT INTO message_processed(id) VALUES ('msg_123') ON DUPLICATE KEY IGNORE;
Redis 去重:用
SETNX
设置消息 ID 过期时间(适合高频场景)。SETNX msg_123 1 EX 3600 # 1 小时内不重复处理
主流 MQ 处理
消息队列 | 重复触发场景 | 推荐方案 |
---|---|---|
Kafka | 消费者重启导致 offset 回滚 | 业务幂等 + 本地 offset 持久化 |
RabbitMQ | 未 ACK 导致重新入队 | 手动 ACK + 死信队列监控 |
RocketMQ | 消息重试机制(16 次后进死信) | 消费日志 + 人工干预 |
极端情况兜底:
- 对账系统:定时扫描业务数据与消息记录,修复不一致(如定时补发短信)。
- 人工告警:监控重复消息频率(如 1 分钟同消息 ID 出现 3 次以上则报警)。
方案选型
- 低频业务:数据库唯一索引(简单可靠)。
- 高频业务:Redis + 过期时间(高性能)。
- 金融级场景:幂等 + 对账 + 人工审核(强一致)。
【困难】如何保证 MQ 消息的顺序性?🌟🌟🌟
要保证 MQ 消息的顺序性,需从 生产、存储、消费 三个环节控制。
核心思路是:“同一业务 ID 锁定同一队列 + 单线程消费”,需结合业务需求选择局部顺序或全局顺序方案。

生产端保序
单生产者+单线程发送:同一业务 ID(如订单 ID)的消息由 同一生产者线程 顺序发送,避免多线程并发乱序。
// 示例:相同 orderId 的消息由同一线程发送 mqProducer.send(msg, orderId); // 通过 hash 选择分区
禁用异步发送重试:异步发送失败时可能乱序,需同步发送或关闭重试(如 Kafka 配置
max.in.flight.requests.per.connection=1
)。
MQ 服务端保序
单分区/队列有序
Kafka/RocketMQ:同一业务 ID 的消息发送到 同一分区(Partition)。
// 根据 orderId 哈希选择分区 int partition = orderId.hashCode() % partitionNum;
RabbitMQ:使用单队列(或一致性哈希交换器绑定唯一队列)。
关闭分区/队列并行:避免服务端多分区/多副本间的顺序混乱(如 Kafka 的
unclean.leader.election.enable=false
)。
消费端保序
- 单消费者串行消费
- 同一队列/分区由 单消费者线程 处理(如 Kafka 单线程消费或
max.poll.records=1
)。 - 多消费者时,相同业务 ID 的消息路由到同一消费者(如 RocketMQ 的
MessageQueueSelector
)。
- 同一队列/分区由 单消费者线程 处理(如 Kafka 单线程消费或
- 内存队列排序(复杂场景):消费者拉取消息后,按业务 ID 分组存入内存队列,由不同线程分别串行处理。
特殊场景处理
- 全局严格顺序:牺牲性能,全链路单线程(生产→MQ→消费),仅适合低吞吐场景(如 Binlog 同步)。
- 局部顺序:仅保证同一业务 ID 的顺序(如订单的创建→支付→退款),允许不同订单并发。
主流 MQ 处理
Kafka | RocketMQ | |
---|---|---|
单分区/队列有序 | 单分区追加写入,天然有序 | 单队列有序 |
哈希路由 | 采用哈希路由,相同 key 固定发往同一分区 如果不指定 key,则采用轮询方式选择分区 | 使用 MessageQueueSelector 指定队列 |
单线程生产 | max.in.flight.requests.per.connection=1 控制 | |
单线程消费 | 确保一个分区仅由一个消费者线程处理(Kafka 默认规则) | 使用 MessageListenerOrderly |
业务保证 | 避免多线程并发消费同一队列 分布式锁(如 Redis) 关键业务操作加锁,防止并发执行乱序 | 避免多线程并发消费同一队列 分布式锁(如 Redis) 关键业务操作加锁,防止并发执行乱序 |
注意事项
- 性能权衡:顺序性越高,并发性能越低(需根据业务容忍度平衡)。
- 错误处理:消费失败时需暂停当前分区消费(如 Kafka 的
pause()
),避免跳过消息导致乱序。 - 监控:定期检查消息积压和顺序偏移(如 Kafka 的
consumer.position()
)。
【困难】如何处理 MQ 消息积压?🌟🌟
处理 MQ 消息积压的核心思路是 “快速消费存量+优化生产速率”,需结合监控、扩容、降级等手段综合治理。
大致可以归纳为:
- 短期:扩容+降级,优先恢复服务。
- 长期:优化消费逻辑+自动化运维,预防再次积压。
- 口诀:监控早发现,扩容扛流量,消费改批量,生产限流速。
快速消费积压消息
- 增加消费者实例:横向扩展消费者服务(如 Kubernetes 动态扩容 Pod),注意分区数限制(Kafka 需提前规划足够分区)。
- 提升消费并行度:
- 调整消费者并发参数(如 Kafka 的
max.poll.records
、RabbitMQ 的prefetch_count
)。 - 多线程消费(需保证无顺序要求的场景)。
- 调整消费者并发参数(如 Kafka 的
- 临时降级:非核心业务暂停消费(如日志处理),集中资源处理核心业务消息。
优化消费能力
- 批量处理:合并多条消息一次处理(如数据库批量插入)。
- 异步化+削峰:消费者将消息存入内存队列,后台线程异步处理,避免同步阻塞。
- 跳过非关键逻辑:临时关闭日志记录、数据校验等非必要操作。
控制生产端流量
- 限流:生产端启用速率限制(如 Kafka 的
quota
、Redis 令牌桶)。 - 削峰填谷:消息先写入缓存层(如 Redis List),再匀速写入 MQ。
- 业务降级:高峰期关闭非核心功能的消息生产(如暂停推荐系统更新)。
监控与告警
- 实时监控指标:
- 队列堆积量(如 Kafka 的
lag
)、消费速率(TPS)、消费者状态。 - 设置阈值告警(如积压超过 10W 条触发短信通知)。
- 队列堆积量(如 Kafka 的
- 根因分析工具:
- 日志分析(消费者卡顿、GC 问题)。
- 链路追踪(如 SkyWalking 定位慢消费)。
长期预防措施
- 容量规划:根据业务峰值预先扩容分区/队列(如 Kafka 分区数 = 消费者数 × 1.5)。
- 死信队列+重试机制:处理失败的消息转入死信队列,避免阻塞正常消费。
- 自动化扩缩容:基于积压指标动态调整消费者数量(如 K8s HPA)。
主流 MQ 处理
消息队列 | 关键操作 |
---|---|
Kafka | 增加分区+消费者,调整 fetch.max.bytes |
RabbitMQ | 镜像队列扩容,提高 prefetch_count |
RocketMQ | 消费组扩容,启用定时消息延迟消费 |
MQ 架构
【困难】Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?
ActiveMQ
ActiveMQ
是由 Apache
出品,ActiveMQ
是一个完全支持JMS1.1
和 J2EE 1.4
规范的 JMS Provider
实现。它非常快速,支持 多种语言的客户端 和 协议,而且可以非常容易的嵌入到企业的应用环境中,并有许多高级功能。
(a) 主要特性
- 服从 JMS 规范:
JMS
规范提供了良好的标准和保证,包括:同步 或 异步 的消息分发,一次和仅一次的消息分发,消息接收 和 订阅 等等。遵从JMS
规范的好处在于,不论使用什么JMS
实现提供者,这些基础特性都是可用的; - 连接灵活性:
ActiveMQ
提供了广泛的 连接协议,支持的协议有:HTTP/S
,IP
多播,SSL
,TCP
,UDP
等等。对众多协议的支持让ActiveMQ
拥有了很好的灵活性; - 支持的协议种类多:
OpenWire
、STOMP
、REST
、XMPP
、AMQP
; - 持久化插件和安全插件:
ActiveMQ
提供了 多种持久化 选择。而且,ActiveMQ
的安全性也可以完全依据用户需求进行 自定义鉴权 和 授权; - 支持的客户端语言种类多:除了
Java
之外,还有:C/C++
,.NET
,Perl
,PHP
,Python
,Ruby
; - 代理集群:多个
ActiveMQ
代理 可以组成一个 集群 来提供服务; - 异常简单的管理:
ActiveMQ
是以开发者思维被设计的。所以,它并不需要专门的管理员,因为它提供了简单又使用的管理特性。有很多中方法可以 监控ActiveMQ
不同层面的数据,包括使用在JConsole
或者在ActiveMQ
的Web Console
中使用JMX
。通过处理JMX
的告警消息,通过使用 命令行脚本,甚至可以通过监控各种类型的 日志。
(b) 部署环境
ActiveMQ
可以运行在 Java
语言所支持的平台之上。使用 ActiveMQ
需要:
Java JDK
ActiveMQ
安装包
(c) 优点
- 跨平台 (
JAVA
编写与平台无关,ActiveMQ
几乎可以运行在任何的JVM
上); - 可以用
JDBC
:可以将 数据持久化 到数据库。虽然使用JDBC
会降低ActiveMQ
的性能,但是数据库一直都是开发人员最熟悉的存储介质; - 支持
JMS
规范:支持JMS
规范提供的 统一接口; - 支持 自动重连 和 错误重试机制;
- 有安全机制:支持基于
shiro
,jaas
等多种 安全配置机制,可以对Queue/Topic
进行 认证和授权; - 监控完善:拥有完善的 监控,包括
Web Console
,JMX
,Shell
命令行,Jolokia
的RESTful API
; - 界面友善:提供的
Web Console
可以满足大部分情况,还有很多 第三方的组件 可以使用,比如hawtio
;
(d) 缺点
- 社区活跃度不及
RabbitMQ
高; - 根据其他用户反馈,会出莫名其妙的问题,会 丢失消息;
- 目前重心放到
activemq 6.0
产品Apollo
,对5.x
的维护较少; - 不适合用于 上千个队列 的应用场景;
RabbitMQ
RabbitMQ
于 2007
年发布,是一个在 AMQP
(高级消息队列协议) 基础上完成的,可复用的企业消息系统,是当前最主流的消息中间件之一。
(a) 主要特性
- 可靠性:提供了多种技术可以让你在 性能 和 可靠性 之间进行 权衡。这些技术包括 持久性机制、投递确认、发布者证实 和 高可用性机制;
- 灵活的路由:消息在到达队列前是通过 交换机 进行 路由 的。
RabbitMQ
为典型的路由逻辑提供了 多种内置交换机 类型。如果你有更复杂的路由需求,可以将这些交换机组合起来使用,你甚至可以实现自己的交换机类型,并且当做RabbitMQ
的 插件 来使用; - 消息集群:在相同局域网中的多个
RabbitMQ
服务器可以 聚合 在一起,作为一个独立的逻辑代理来使用; - 队列高可用:队列可以在集群中的机器上 进行镜像,以确保在硬件问题下还保证 消息安全;
- 支持多种协议:支持 多种消息队列协议;
- 支持多种语言:用
Erlang
语言编写,支持只要是你能想到的 所有编程语言; - 管理界面:
RabbitMQ
有一个易用的 用户界面,使得用户可以 监控 和 管理 消息Broker
的许多方面; - 跟踪机制:如果 消息异常,
RabbitMQ
提供消息跟踪机制,使用者可以找出发生了什么; - 插件机制:提供了许多 插件,来从多方面进行扩展,也可以编写自己的插件。
(b) 部署环境
RabbitMQ
可以运行在 Erlang
语言所支持的平台之上,包括 Solaris
,BSD
,Linux
,MacOSX
,TRU64
,Windows
等。使用 RabbitMQ
需要:
ErLang
语言包RabbitMQ
安装包
(c) 优点
- 由于
Erlang
语言的特性,消息队列性能较好,支持 高并发; - 健壮、稳定、易用、跨平台、支持 多种语言、文档齐全;
- 有消息 确认机制 和 持久化机制,可靠性高;
- 高度可定制的 路由;
- 管理界面 较丰富,在互联网公司也有较大规模的应用,社区活跃度高。
(d) 缺点
- 尽管结合
Erlang
语言本身的并发优势,性能较好,但是不利于做 二次开发和维护; - 实现了 代理架构,意味着消息在发送到客户端之前可以在 中央节点 上排队。此特性使得
RabbitMQ
易于使用和部署,但是使得其 运行速度较慢,因为中央节点 增加了延迟,消息封装后 也比较大; - 需要学习 比较复杂 的 接口和协议,学习和维护成本较高。
RocketMQ
RocketMQ
出自 阿里 的开源产品,用 Java
语言实现,在设计时参考了 Kafka
,并做出了自己的一些改进,消息可靠性上 比 Kafka
更好。RocketMQ
在阿里内部 被广泛应用在 订单,交易,充值,流计算,消息推送,日志流式处理,binglog
分发 等场景。
(a) 主要特性
- 基于 队列模型:具有 高性能、高可靠、高实时、分布式 等特点;
Producer
、Consumer
、队列 都支持 分布式;Producer
向一些队列轮流发送消息,队列集合 称为Topic
。Consumer
如果做 广播消费,则一个Consumer
实例消费这个Topic
对应的 所有队列;如果做 集群消费,则 多个Consumer
实例 平均消费 这个Topic
对应的队列集合;- 能够保证 严格的消息顺序;
- 提供丰富的 消息拉取模式;
- 高效的订阅者 水平扩展能力;
- 实时 的 消息订阅机制;
- 亿级 消息堆积 能力;
- 较少的外部依赖。
(b) 部署环境
RocketMQ
可以运行在 Java
语言所支持的平台之上。使用 RocketMQ
需要:
Java JDK
- 安装
git
、Maven
RocketMQ
安装包
(c) 优点
- 单机 支持
1
万以上 持久化队列; RocketMQ
的所有消息都是 持久化的,先写入系统PAGECACHE
,然后 刷盘,可以保证 内存 与 磁盘 都有一份数据,而 访问 时,直接 从内存读取。- 模型简单,接口易用(
JMS
的接口很多场合并不太实用); - 性能非常好,可以允许 大量堆积消息 在
Broker
中; - 支持 多种消费模式,包括 集群消费、广播消费等;
- 各个环节 分布式扩展设计,支持 主从 和 高可用;
- 开发度较活跃,版本更新很快。
(d) 缺点
- 支持的 客户端语言 不多,目前是
Java
及C++
,其中C++
还不成熟; RocketMQ
社区关注度及成熟度也不及前两者;- 没有
Web
管理界面,提供了一个CLI
(命令行界面) 管理工具带来 查询、管理 和 诊断各种问题; - 没有在
MQ
核心里实现JMS
等接口;
Kafka
Apache Kafka
是一个 分布式消息发布订阅 系统。它最初由 LinkedIn
公司基于独特的设计实现为一个 分布式的日志提交系统 (a distributed commit log
),之后成为 Apache
项目的一部分。Kafka
性能高效、可扩展良好 并且 可持久化。它的 分区特性,可复制 和 可容错 都是其不错的特性。
(a) 主要特性
- 快速持久化:可以在
O(1)
的系统开销下进行 消息持久化; - 高吞吐:在一台普通的服务器上既可以达到
10W/s
的 吞吐速率; - 完全的分布式系统:
Broker
、Producer
和Consumer
都原生自动支持 分布式,自动实现 负载均衡; - 支持 同步 和 异步 复制两种 高可用机制;
- 支持 数据批量发送 和 拉取;
- 零拷贝技术 (zero-copy):减少
IO
操作步骤,提高 系统吞吐量; - 数据迁移、扩容 对用户透明;
- 无需停机 即可扩展机器;
- 其他特性:丰富的 消息拉取模型、高效 订阅者水平扩展、实时的 消息订阅、亿级的 消息堆积能力、定期删除机制;
(b) 部署环境
使用 Kafka
需要:
Java JDK
Kafka
安装包
(c) 优点
- 客户端语言丰富:支持
Java
、.Net
、PHP
、Ruby
、Python
、Go
等多种语言; - 高性能:单机写入
TPS
约在100
万条/秒,消息大小10
个字节; - 提供 完全分布式架构,并有
replica
机制,拥有较高的 可用性 和 可靠性,理论上支持 消息无限堆积; - 支持批量操作;
- 消费者 采用
Pull
方式获取消息。消息有序,通过控制 能够保证所有消息被消费且仅被消费 一次; - 有优秀的第三方
Kafka Web
管理界面Kafka-Manager
; - 在 日志领域 比较成熟,被多家公司和多个开源项目使用。
(d) 缺点
Kafka
单机超过64
个 队列/分区 时,Load
时会发生明显的飙高现象。队列 越多,负载 越高,发送消息 响应时间变长;- 使用 短轮询方式,实时性 取决于 轮询间隔时间;
- 消费失败 不支持重试;
- 支持 消息顺序,但是 一台代理宕机 后,就会产生 消息乱序;
- 社区更新较慢。
技术选型
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
---|---|---|---|---|
单机吞吐量 | 万级,比 RocketMQ、Kafka 低一个数量级 | 同 ActiveMQ | 10 万级,支撑高吞吐 | 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景 |
topic 数量对吞吐量的影响 | topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic | topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源 | ||
时效性 | ms 级 | 微秒级,这是 RabbitMQ 的一大特点,延迟最低 | ms 级 | 延迟在 ms 级以内 |
可用性 | 高,基于主从架构实现高可用 | 同 ActiveMQ | 非常高,分布式架构 | 非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |
消息可靠性 | 有较低的概率丢失数据 | 基本不丢 | 经过参数优化配置,可以做到 0 丢失 | 同 RocketMQ |
功能支持 | MQ 领域的功能极其完备 | 基于 erlang 开发,并发能力很强,性能极好,延时很低 | MQ 功能较为完善,还是分布式的,扩展性好 | 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用 |
综上,各种对比之后,有如下建议:
- 一般的业务系统要引入 MQ,最早大家都用 ActiveMQ,但是现在确实大家用的不多了,没经过大规模吞吐量场景的验证,社区也不是很活跃,所以大家还是算了吧,我个人不推荐用这个了;
- 后来大家开始用 RabbitMQ,但是确实 erlang 语言阻止了大量的 Java 工程师去深入研究和掌控它,对公司而言,几乎处于不可控的状态,但是确实人家是开源的,比较稳定的支持,活跃度也高;
- 不过现在确实越来越多的公司会去用 RocketMQ,确实很不错,毕竟是阿里出品,但社区可能有突然黄掉的风险(目前 RocketMQ 已捐给 Apache,但 GitHub 上的活跃度其实不算高)对自己公司技术实力有绝对自信的,推荐用 RocketMQ,否则回去老老实实用 RabbitMQ 吧,人家有活跃的开源社区,绝对不会黄。
- 所以中小型公司,技术实力较为一般,技术挑战不是特别高,用 RabbitMQ 是不错的选择;大型公司,基础架构研发实力较强,用 RocketMQ 是很好的选择。
- 如果是大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,绝对没问题,社区活跃度很高,绝对不会黄,何况几乎是全世界这个领域的事实性规范。
【困难】什么是 JMS?
提到 MQ,就顺便提一下 JMS 。
JMS(JAVA Message Service,java 消息服务)API 是一个消息服务的标准/规范,允许应用程序组件基于 JavaEE 平台创建、发送、接收和读取消息。它使分布式通信耦合度更低,消息服务更加可靠以及异步性。
在 EJB 架构中,有消息 bean 可以无缝的与 JMS 消息服务集成。在 J2EE 架构模式中,有消息服务者模式,用于实现消息与应用直接的解耦。
JMS 消息模型
在 JMS 标准中,有两种消息模型:
- P2P(Point to Point)
- Pub/Sub(Publish/Subscribe)
P2P 模式

P2P 模式包含三个角色:MQ(Queue),发送者 (Sender),接收者 (Receiver)。每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。
P2P 的特点
- 每个消息只有一个消费者(Consumer)(即一旦被消费,消息就不再在 MQ 中)
- 发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列
- 接收者在成功接收消息之后需向队列应答成功
如果希望发送的每个消息都会被成功处理的话,那么需要 P2P 模式。
Pub/sub 模式

包含三个角色主题(Topic),发布者(Publisher),订阅者(Subscriber) 。多个发布者将消息发送到 Topic, 系统将这些消息传递给多个订阅者。
Pub/Sub 的特点
- 每个消息可以有多个消费者
- 发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息。
- 为了消费消息,订阅者必须保持运行的状态。
为了缓和这样严格的时间相关性,JMS 允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。
如果希望发送的消息可以不被做任何处理、或者只被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用 Pub/Sub 模型。
JMS 消息消费
在 JMS 中,消息的产生和消费都是异步的。对于消费来说,JMS 的消息者可以通过两种方式来消费消息。
- 同步 - 订阅者或接收者通过
receive
方法来接收消息,receive
方法在接收到消息之前(或超时之前)将一直阻塞; - 异步 - 订阅者或接收者可以注册为一个消息监听器。当消息到达之后,系统自动调用监听器的
onMessage
方法。
JNDI
- Java 命名和目录接口,是一种标准的 Java 命名系统接口。可以在网络上查找和访问服务。通过指定一个资源名称,该名称对应于数据库或命名服务中的一个记录,同时返回资源连接建立所必须的信息。
JNDI 在 JMS 中起到查找和访问发送目标或消息来源的作用。
【中等】什么是 AMQP?
AMQP(Advanced Message Queuing Protocol)是一种应用层协议,用于在消息队列系统中定义消息的格式、传输方式和处理机制。
AMQP 是一个面向消息的、异步传输的协议,具有高可靠性、可拓展性、跨平台的特性,适合在分布式系统中传输重要数据。它是 RabbitMQ、ActiveMQ 等消息中间件的底层协议。
核心组件
组件 | 作用 |
---|---|
Connection | 客户端与消息代理(如 RabbitMQ)的物理连接 |
Channel | 逻辑通信链路(多路复用连接,减少开销) |
Exchange | 消息路由枢纽(支持多种路由策略) |
Queue | 存储消息的容器,消费者从中拉取数据 |
Binding | 定义 Exchange 与 Queue 的映射关系(含路由规则) |
路由模型
- Direct:精确匹配路由键(如
order.create
) - Fanout:广播到所有绑定队列(无视路由键)
- Topic:通配符匹配(如
order.*
) - Headers:基于消息头键值匹配(非路由键)
可靠性保障
- 消息确认:消费者手动确认,失败则重入队列
- 持久化:队列/消息持久化防丢失
- 事务:支持原子性提交/回滚(批量操作)
协议对比
协议 | 优势场景 | 局限性 |
---|---|---|
AMQP | 企业级应用(强事务、高可靠) | 略重(不适合 IoT 轻量场景) |
MQTT | 物联网(低功耗、低带宽) | 功能简单(无复杂路由) |
JMS | Java 生态集成 | 仅限 Java,跨平台性弱 |
主流 MQ
【中等】RocketMQ 和 Kafka 在架构和功能上有什么区别?
RocketMQ 和 Kafka 架构对比
维度 | RocketMQ | Kafka |
---|---|---|
注册中心 | 自研轻量级 NameServer(AP 模型) | 依赖 ZooKeeper(CP 模型),Kafka 新版本计划用 KRaft 取代 ZooKeeper |
Broker 角色 | 主从架构(Master-Slave) | 无主从区分(所有节点平等) |
存储模型 | 单个 CommitLog 文件(含所有 Topic 数据) + 消费队列索引 | 主题-分区-段的层级结构 |
消费模式 | 支持 Push/Pull 两种模式 | 仅 Pull 模式 |
RocketMQ 和 Kafka 功能对比
设计差异:
- RocketMQ:
- 业务友好:强事务、延迟消息、过滤等开箱即用
- 轻量可控:NameServer 简化集群管理
- Kafka:
- 流式优先:高吞吐、分区弹性伸缩
- 生态整合:与 Flink/Spark 等深度兼容
主要功能差异如下:
功能 | RocketMQ | Kafka |
---|---|---|
消息类型 | 普通消息、顺序消息、事务消息、延迟消息 | 主要支持普通消息(需扩展实现其他特性) |
事务支持 | 二阶段提交+事务回查(业务级事务) | 精确一次语义(内部消息事务) |
延迟消息 | 内置 18 个固定延迟级别 | 需外部实现(如流处理或自定义调度) |
消息回溯 | 支持按时间/偏移量回溯 | 仅支持按偏移量回溯 |
消息过滤 | 支持给 Topic 打标签,进一步分类 | 仅支持通过 Topic 给消息分类 |
死信队列 | 消息消费重试失败多次后,进入死信队列 | Kafka 原生不支持 |
RocketMQ 和 Kafka 性能与扩展对比
维度 | RocketMQ | Kafka |
---|---|---|
吞吐量 | 单机约 10 万 TPS | 单机可达百万 TPS(更高吞吐) |
延迟 | 毫秒级(优化后更低) | 毫秒级(依赖 PageCache) |
水平扩展 | 需手动调整队列数 | 分区自动均衡(更易扩展) |
数据保留 | 基于时间/大小策略 | 基于时间/大小策略(支持日志压缩) |
RocketMQ 和 Kafka 应用场景与选型
场景 | RocketMQ | Kafka |
---|---|---|
电商/金融业务 | 订单、支付等业务级事务 | 日志收集、流处理(如 Flink) |
延迟/定时任务 | 内置支持(如订单超时) | 需外部系统配合 |
大规模日志流 | 适合中小规模 | 超大规模日志场景(如大数据管道) |
多协议支持 | 兼容 JMS、MQTT 等 | 仅原生协议 |
总结选择建议:
- 选 RocketMQ:需要 业务级事务、延迟消息、多协议支持 的场景
- 选 Kafka:需要 超高吞吐、流处理集成、大规模日志 的场景