跳至主要內容

MQ 面试

钝悟...大约 49 分钟分布式分布式通信MQ分布式通信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 写库耗时分别为:100ms200ms300ms。最终总耗时为: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 数据进行可视化展示,或自行进行定制化开发。

img
img

(2)MQ 也可以被用于流式处理。

例如,Kafka 几乎已经是流计算的数据采集端的标准组件。而流计算通过实时数据处理能力,提供了更为快捷的聚合计算能力,被大量应用于链路监控、实时监控、实时数仓、实时大屏、风控、推荐等应用领域。

最终一致性

最终一致性 不是 消息队列 的必备特性,但确实可以依靠 消息队列 来做 最终一致性 的事情。

  • 先写消息再操作,确保操作完成后再修改消息状态。定时任务补偿机制 实现消息 可靠发送接收、业务操作的可靠执行,要注意 消息重复幂等设计
  • 所有不保证 100% 不丢消息 的消息队列,理论上无法实现 最终一致性

Kafka 一类的设计,在设计层面上就有 丢消息 的可能(比如 定时刷盘,如果掉电就会丢消息)。哪怕只丢千分之一的消息,业务也必须用其他的手段来保证结果正确。

【中等】MQ 存在哪些挑战?🌟

任何技术都会有利有弊,MQ 给整体系统架构带来很多好处,但也会付出一定的代价。

MQ 主要引入了以下问题:

  • 系统可用性降低:引入了 MQ 后,通信需要基于 MQ 完成,如果 MQ 宕机,则服务不可用。因此,MQ 要保证是高可用的。
  • 系统复杂度提高:使用 MQ,需要关注一些新的问题:
    • 如何保证消息没有 重复消费
    • 如何处理 消息丢失 的问题?
    • 如何保证传递 消息的顺序性
    • 如何处理大量 消息积压 的问题?
  • 一致性问题:假设系统 A 处理完直接返回成功的结果给用户,用户认为请求成功。但如果此时,系统 BCD 中只要有任意一个写库失败,那么数据就不一致了。这种情况如何处理?

【中等】Kafka、RocketMq、RabbitMQ 如何选型?🌟

特点与适用场景对比

类别KafkaRocketMQRabbitMQ
核心优势超高吞吐量
极佳的海量数据堆积能力
强大的生态集成
高吞吐与低延迟兼备
金融级事务消息
阿里系生产环境考验
灵活的路由功能
丰富的企业级特性
强大的管理界面与多协议支持
主要劣势功能相对单一,路由能力弱
对于简单业务,部署和运维相对复杂
社区和生态相对于 Kafka 较小
命名与 Kafka 有差异,学习有成本
吞吐量和堆积能力远低于前两者
性能受内存和磁盘碎片影响更大
典型适用场景日志收集与聚合
流式处理与实时数据管道
事件溯源
大数据领域
电商、金融等核心交易系统
高并发订单处理
对事务一致性要求高的场景
企业级应用集成(复杂路由)
任务分发、RPC
对吞吐要求不极高的业务解耦
不适用场景需要复杂路由、低延迟(毫秒级)响应的业务需要与 Kafka 生态紧密集成的项目需要海量消息堆积和高吞吐量的场景

MQ 存储

【中等】Kafka、RocketMq、RabbitMQ 如何存储数据?🌟🌟

逻辑与物理存储模型对比

特性KafkaRocketMQRabbitMQ
核心逻辑模型发布/订阅的日志流发布/订阅的消息队列队列与交换器的路由代理
逻辑存储主题(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 如何持久化?🌟🌟

核心要点对比

机制KafkaRocketMQRabbitMQ
核心设计日志即存储读写分离队列即存储
写入方式顺序追加写入顺序追加写入 (CommitLog)可变长度写入
刷盘机制异步(高性能)
同步(高可靠)
异步 (高性能)
同步 (高可靠)
异步
确认后删除
数据清理基于保留策略
时间
大小
压缩 (Key 去重)
基于保留策略
时间
大小
消费后删除
确认后立即删除
队列长度限制
索引机制稀疏索引
.index(偏移量)
.timeindex(时间戳)
多级索引
ConsumeQueue(核心逻辑索引)
IndexFile(关键字查询)
队列索引
消息存储索引
内存状态记录(性能瓶颈)

总结

  • Kafka:为超高吞吐和海量数据堆积设计,采用最极致的顺序 I/O,像一个只增不删的日志系统。
  • RocketMQ:在 Kafka 的基础上做了优化,通过读写分离的设计,在保证高吞吐写入的同时,兼顾了低延迟读取事务消息等金融级需求。
  • RabbitMQ:核心在于灵活的路由和可靠交付,其存储设计不适合海量消息堆积,但能实现复杂的消息路由和“确认即删除”的精准控制。

MQ 生产消费

【中等】MQ 数据传输有哪些模式?🌟🌟

MQ 数据传输的模式主要分为 Push(推)Pull(拉) 两种,不同消息队列中间件采用不同的策略,部分系统还支持 混合模式

消息队列消费模式对比:

维度PushPull长轮询混合模式
特点Broker 主动推送给消费者,消费者被动接收;实时性高,减少消费者轮询开销消费者主动从 Broker 拉取消息,按需获取;消费者控制消费速率Push 和 Pull 的折中方案;请求被保持,直到有消息或超时关键消息用 Push批量数据用 Pull;消费者可动态切换模式
优点低延迟,适合实时性要求高的场景(如即时通讯)避免消息堆积冲击消费者,适合高吞吐场景(如日志处理)减少无效轮询,平衡实时性与服务端压力灵活性强,能兼顾实时性和吞吐量,适应复杂场景
缺点可能造成消费者过载(需背压机制控制流速)存在空轮询开销(可通过长轮询优化)实现复杂度高于纯 Pull,需维护挂起的请求连接系统设计和实现的复杂度最高
典型实现RabbitMQ、ActiveMQ、RocketMQ(默认长轮询模拟 Push)KafkaPulsar(原生 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 生产消费的不同点

特性维度KafkaRocketMQRabbitMQ
生产/消费模型基于日志的发布/订阅基于队列的发布/订阅灵活的路由 (Exchange -> Queue)
同步/异步发送同步send().get()
异步send(callback)
单向send() 无回调(不关心结果)
同步send() 阻塞
异步sendAsync(callback)
单向sendOneway()
同步:使用 Channel 的确认机制
异步:使用 Confirm Mode 和回调
推拉模型消费者主动拉取消费者主动拉取Broker 主动推送 (默认)
(也支持拉取 Get,但不常用)
是否支持批量生产是,核心功能
生产者会累积消息,批量发送到同一 Partition,极大提升吞吐
是,核心功能
支持将多条消息打包成一个批次发送
是,但较弱
通过 Publisher ConfirmsTx 模拟,或使用第三方插件。原生对批量不友好
是否支持批量消费是,核心功能
消费者一次拉取一批消息进行处理。
是,核心功能
消费者可以设置一次拉取的消息条数
是,通过 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 副本机制有什么不同之处?🌟🌟🌟

副本机制对比

特性维度KafkaRocketMQRabbitMQ
核心模型主从复制主从复制镜像队列 (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 分区机制有什么不同之处?🌟🌟🌟

核心分区/分片机制对比

特性维度KafkaRocketMQRabbitMQ
核心概念分区队列队列
逻辑实体TopicTopicExchange + 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+监控补偿 是保证消息不丢失的核心逻辑,需根据业务场景权衡性能与可靠性。

生产端防丢失

KafkaRocketMQ
发送方式支持同步、异步、异步回调发送方式
同步性能低;异步无视成功与否;异步回调比较合适
支持同步、异步回调发送方式
同步性能低;异步回调比较合适
生产 ACKacks 参数确保消息被多少副本写入成功后才返回确认
min.insync.replicas 配合 acks 使用,设定最少写入副本数
(配合刷盘机制)若配置为同步刷盘,消息必须先成功写入磁盘,发送方才能收到写成功的确认
失败重试retries 参数控制失败重试次数retryTimesWhenSendFailed 参数控制失败重试次数
事务通过事务+生产幂等,实现 exactly once 语义,非真正的分布式事务可实现分布式事务

MQ 服务端防丢失

KafkaRocketMQ
持久化消息写入页缓存(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 来选主

消费端防丢失

KafkaRocketMQ
消费 ACK支持自动、手动提交 Offset(enable.auto.commit=false配置)
关闭自动提交,处理完整事务后,再手动提交 Offset
支持自动、手动提交 Offset(consumer.setAutoCommit 配置)
关闭自动提交,处理完整事务后,再手动提交 Offset
重置 Offsetauto.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)。
  • 内存队列排序(复杂场景):消费者拉取消息后,按业务 ID 分组存入内存队列,由不同线程分别串行处理。

特殊场景处理

  • 全局严格顺序:牺牲性能,全链路单线程(生产→MQ→消费),仅适合低吞吐场景(如 Binlog 同步)。
  • 局部顺序:仅保证同一业务 ID 的顺序(如订单的创建→支付→退款),允许不同订单并发。

主流 MQ 处理

KafkaRocketMQ
单分区/队列有序单分区追加写入,天然有序单队列有序
哈希路由采用哈希路由,相同 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 的 quota、Redis 令牌桶)。
  • 削峰填谷:消息先写入缓存层(如 Redis List),再匀速写入 MQ。
  • 业务降级:高峰期关闭非核心功能的消息生产(如暂停推荐系统更新)。

监控与告警

  • 实时监控指标
    • 队列堆积量(如 Kafka 的 lag)、消费速率(TPS)、消费者状态。
    • 设置阈值告警(如积压超过 10W 条触发短信通知)。
  • 根因分析工具
    • 日志分析(消费者卡顿、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.1J2EE 1.4 规范的 JMS Provider 实现。它非常快速,支持 多种语言的客户端协议,而且可以非常容易的嵌入到企业的应用环境中,并有许多高级功能。

(a) 主要特性

  1. 服从 JMS 规范JMS 规范提供了良好的标准和保证,包括:同步异步 的消息分发,一次和仅一次的消息分发,消息接收订阅 等等。遵从 JMS 规范的好处在于,不论使用什么 JMS 实现提供者,这些基础特性都是可用的;
  2. 连接灵活性ActiveMQ 提供了广泛的 连接协议,支持的协议有:HTTP/SIP 多播SSLTCPUDP 等等。对众多协议的支持让 ActiveMQ 拥有了很好的灵活性;
  3. 支持的协议种类多OpenWireSTOMPRESTXMPPAMQP
  4. 持久化插件和安全插件ActiveMQ 提供了 多种持久化 选择。而且,ActiveMQ 的安全性也可以完全依据用户需求进行 自定义鉴权授权
  5. 支持的客户端语言种类多:除了 Java 之外,还有:C/C++.NETPerlPHPPythonRuby
  6. 代理集群:多个 ActiveMQ 代理 可以组成一个 集群 来提供服务;
  7. 异常简单的管理ActiveMQ 是以开发者思维被设计的。所以,它并不需要专门的管理员,因为它提供了简单又使用的管理特性。有很多中方法可以 监控 ActiveMQ 不同层面的数据,包括使用在 JConsole 或者在 ActiveMQWeb Console 中使用 JMX。通过处理 JMX 的告警消息,通过使用 命令行脚本,甚至可以通过监控各种类型的 日志

(b) 部署环境

ActiveMQ 可以运行在 Java 语言所支持的平台之上。使用 ActiveMQ 需要:

  • Java JDK
  • ActiveMQ 安装包

(c) 优点

  1. 跨平台 (JAVA 编写与平台无关,ActiveMQ 几乎可以运行在任何的 JVM 上);
  2. 可以用 JDBC:可以将 数据持久化 到数据库。虽然使用 JDBC 会降低 ActiveMQ 的性能,但是数据库一直都是开发人员最熟悉的存储介质;
  3. 支持 JMS 规范:支持 JMS 规范提供的 统一接口;
  4. 支持 自动重连错误重试机制
  5. 有安全机制:支持基于 shirojaas 等多种 安全配置机制,可以对 Queue/Topic 进行 认证和授权
  6. 监控完善:拥有完善的 监控,包括 Web ConsoleJMXShell 命令行,JolokiaRESTful API
  7. 界面友善:提供的 Web Console 可以满足大部分情况,还有很多 第三方的组件 可以使用,比如 hawtio

(d) 缺点

  1. 社区活跃度不及 RabbitMQ 高;
  2. 根据其他用户反馈,会出莫名其妙的问题,会 丢失消息
  3. 目前重心放到 activemq 6.0 产品 Apollo,对 5.x 的维护较少;
  4. 不适合用于 上千个队列 的应用场景;

RabbitMQ

RabbitMQ2007 年发布,是一个在 AMQP (高级消息队列协议) 基础上完成的,可复用的企业消息系统,是当前最主流的消息中间件之一。

(a) 主要特性

  1. 可靠性:提供了多种技术可以让你在 性能可靠性 之间进行 权衡。这些技术包括 持久性机制投递确认发布者证实高可用性机制
  2. 灵活的路由:消息在到达队列前是通过 交换机 进行 路由 的。RabbitMQ 为典型的路由逻辑提供了 多种内置交换机 类型。如果你有更复杂的路由需求,可以将这些交换机组合起来使用,你甚至可以实现自己的交换机类型,并且当做 RabbitMQ插件 来使用;
  3. 消息集群:在相同局域网中的多个 RabbitMQ 服务器可以 聚合 在一起,作为一个独立的逻辑代理来使用;
  4. 队列高可用:队列可以在集群中的机器上 进行镜像,以确保在硬件问题下还保证 消息安全
  5. 支持多种协议:支持 多种消息队列协议
  6. 支持多种语言:用 Erlang 语言编写,支持只要是你能想到的 所有编程语言
  7. 管理界面RabbitMQ 有一个易用的 用户界面,使得用户可以 监控管理 消息 Broker 的许多方面;
  8. 跟踪机制:如果 消息异常RabbitMQ 提供消息跟踪机制,使用者可以找出发生了什么;
  9. 插件机制:提供了许多 插件,来从多方面进行扩展,也可以编写自己的插件。

(b) 部署环境

RabbitMQ 可以运行在 Erlang 语言所支持的平台之上,包括 SolarisBSDLinuxMacOSXTRU64Windows 等。使用 RabbitMQ 需要:

  • ErLang 语言包
  • RabbitMQ 安装包

(c) 优点

  1. 由于 Erlang 语言的特性,消息队列性能较好,支持 高并发
  2. 健壮、稳定、易用、跨平台、支持 多种语言、文档齐全;
  3. 有消息 确认机制持久化机制,可靠性高;
  4. 高度可定制的 路由
  5. 管理界面 较丰富,在互联网公司也有较大规模的应用,社区活跃度高。

(d) 缺点

  1. 尽管结合 Erlang 语言本身的并发优势,性能较好,但是不利于做 二次开发和维护
  2. 实现了 代理架构,意味着消息在发送到客户端之前可以在 中央节点 上排队。此特性使得 RabbitMQ 易于使用和部署,但是使得其 运行速度较慢,因为中央节点 增加了延迟消息封装后 也比较大;
  3. 需要学习 比较复杂接口和协议,学习和维护成本较高。

RocketMQ

RocketMQ 出自 阿里 的开源产品,用 Java 语言实现,在设计时参考了 Kafka,并做出了自己的一些改进,消息可靠性上Kafka 更好。RocketMQ 在阿里内部  被广泛应用在 订单交易充值流计算消息推送日志流式处理binglog 分发 等场景。

(a) 主要特性

  1. 基于 队列模型:具有 高性能高可靠高实时分布式 等特点;
  2. ProducerConsumer队列 都支持 分布式
  3. Producer 向一些队列轮流发送消息,队列集合 称为 TopicConsumer 如果做 广播消费,则一个 Consumer 实例消费这个 Topic 对应的 所有队列;如果做 集群消费,则 多个 Consumer 实例 平均消费 这个 Topic 对应的队列集合;
  4. 能够保证 严格的消息顺序
  5. 提供丰富的 消息拉取模式
  6. 高效的订阅者 水平扩展能力;
  7. 实时消息订阅机制
  8. 亿级 消息堆积 能力;
  9. 较少的外部依赖。

(b) 部署环境

RocketMQ 可以运行在 Java 语言所支持的平台之上。使用 RocketMQ 需要:

  • Java JDK
  • 安装 gitMaven
  • RocketMQ 安装包

(c) 优点

  1. 单机 支持 1 万以上 持久化队列
  2. RocketMQ 的所有消息都是 持久化的,先写入系统 PAGECACHE,然后 刷盘,可以保证 内存磁盘 都有一份数据,而 访问 时,直接 从内存读取
  3. 模型简单,接口易用(JMS 的接口很多场合并不太实用);
  4. 性能非常好,可以允许 大量堆积消息Broker 中;
  5. 支持 多种消费模式,包括 集群消费广播消费等;
  6. 各个环节 分布式扩展设计,支持 主从高可用
  7. 开发度较活跃,版本更新很快。

(d) 缺点

  1. 支持的 客户端语言 不多,目前是 JavaC++,其中 C++ 还不成熟;
  2. RocketMQ 社区关注度及成熟度也不及前两者;
  3. 没有 Web 管理界面,提供了一个 CLI (命令行界面) 管理工具带来 查询管理诊断各种问题
  4. 没有在 MQ 核心里实现 JMS 等接口;

Kafka

Apache Kafka 是一个 分布式消息发布订阅 系统。它最初由 LinkedIn 公司基于独特的设计实现为一个 分布式的日志提交系统 (a distributed commit log),之后成为 Apache 项目的一部分。Kafka 性能高效可扩展良好 并且 可持久化。它的 分区特性可复制可容错 都是其不错的特性。

(a) 主要特性

  1. 快速持久化:可以在 O(1) 的系统开销下进行 消息持久化
  2. 高吞吐:在一台普通的服务器上既可以达到 10W/s吞吐速率
  3. 完全的分布式系统BrokerProducerConsumer 都原生自动支持 分布式,自动实现 负载均衡
  4. 支持 同步异步 复制两种 高可用机制
  5. 支持 数据批量发送拉取
  6. 零拷贝技术 (zero-copy):减少 IO 操作步骤,提高 系统吞吐量
  7. 数据迁移扩容 对用户透明;
  8. 无需停机 即可扩展机器;
  9. 其他特性:丰富的 消息拉取模型、高效 订阅者水平扩展、实时的 消息订阅、亿级的 消息堆积能力、定期删除机制;

(b) 部署环境

使用 Kafka 需要:

  • Java JDK
  • Kafka 安装包

(c) 优点

  1. 客户端语言丰富:支持 Java.NetPHPRubyPythonGo 等多种语言;
  2. 高性能:单机写入 TPS 约在 100 万条/秒,消息大小 10 个字节;
  3. 提供 完全分布式架构,并有 replica 机制,拥有较高的 可用性可靠性,理论上支持 消息无限堆积
  4. 支持批量操作;
  5. 消费者 采用 Pull 方式获取消息。消息有序通过控制 能够保证所有消息被消费且仅被消费 一次
  6. 有优秀的第三方 Kafka Web 管理界面 Kafka-Manager
  7. 日志领域 比较成熟,被多家公司和多个开源项目使用。

(d) 缺点

  1. Kafka 单机超过 64队列/分区 时,Load 时会发生明显的飙高现象。队列 越多,负载 越高,发送消息 响应时间变长
  2. 使用 短轮询方式实时性 取决于 轮询间隔时间
  3. 消费失败 不支持重试
  4. 支持 消息顺序,但是 一台代理宕机 后,就会产生 消息乱序
  5. 社区更新较慢。

技术选型

特性ActiveMQRabbitMQRocketMQKafka
单机吞吐量万级,比 RocketMQ、Kafka 低一个数量级同 ActiveMQ10 万级,支撑高吞吐10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景
topic 数量对吞吐量的影响topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topictopic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源
时效性ms 级微秒级,这是 RabbitMQ 的一大特点,延迟最低ms 级延迟在 ms 级以内
可用性高,基于主从架构实现高可用同 ActiveMQ非常高,分布式架构非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用
消息可靠性有较低的概率丢失数据基本不丢经过参数优化配置,可以做到 0 丢失同 RocketMQ
功能支持MQ 领域的功能极其完备基于 erlang 开发,并发能力很强,性能极好,延时很低MQ 功能较为完善,还是分布式的,扩展性好功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用

综上,各种对比之后,有如下建议:

  • 一般的业务系统要引入 MQ,最早大家都用 ActiveMQ,但是现在确实大家用的不多了,没经过大规模吞吐量场景的验证,社区也不是很活跃,所以大家还是算了吧,我个人不推荐用这个了;
  • 后来大家开始用 RabbitMQ,但是确实 erlang 语言阻止了大量的 Java 工程师去深入研究和掌控它,对公司而言,几乎处于不可控的状态,但是确实人家是开源的,比较稳定的支持,活跃度也高;
  • 不过现在确实越来越多的公司会去用 RocketMQ,确实很不错,毕竟是阿里出品,但社区可能有突然黄掉的风险(目前 RocketMQ 已捐给 Apacheopen in new window,但 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物联网(低功耗、低带宽)功能简单(无复杂路由)
JMSJava 生态集成仅限 Java,跨平台性弱

主流 MQ

【中等】RocketMQ 和 Kafka 在架构和功能上有什么区别?

RocketMQ 和 Kafka 架构对比

维度RocketMQKafka
注册中心自研轻量级 NameServer(AP 模型)依赖 ZooKeeper(CP 模型),Kafka 新版本计划用 KRaft 取代 ZooKeeper
Broker 角色主从架构(Master-Slave)无主从区分(所有节点平等)
存储模型单个 CommitLog 文件(含所有 Topic 数据) + 消费队列索引主题-分区-段的层级结构
消费模式支持 Push/Pull 两种模式仅 Pull 模式

RocketMQ 和 Kafka 功能对比

设计差异:

  • RocketMQ
    • 业务友好:强事务、延迟消息、过滤等开箱即用
    • 轻量可控:NameServer 简化集群管理
  • Kafka
    • 流式优先:高吞吐、分区弹性伸缩
    • 生态整合:与 Flink/Spark 等深度兼容

主要功能差异如下:

功能RocketMQKafka
消息类型普通消息、顺序消息、事务消息、延迟消息主要支持普通消息(需扩展实现其他特性)
事务支持二阶段提交+事务回查(业务级事务)精确一次语义(内部消息事务)
延迟消息内置 18 个固定延迟级别需外部实现(如流处理或自定义调度)
消息回溯支持按时间/偏移量回溯仅支持按偏移量回溯
消息过滤支持给 Topic 打标签,进一步分类仅支持通过 Topic 给消息分类
死信队列消息消费重试失败多次后,进入死信队列Kafka 原生不支持

RocketMQ 和 Kafka 性能与扩展对比

维度RocketMQKafka
吞吐量单机约 10 万 TPS单机可达百万 TPS(更高吞吐)
延迟毫秒级(优化后更低)毫秒级(依赖 PageCache)
水平扩展需手动调整队列数分区自动均衡(更易扩展)
数据保留基于时间/大小策略基于时间/大小策略(支持日志压缩)

RocketMQ 和 Kafka 应用场景与选型

场景RocketMQKafka
电商/金融业务订单、支付等业务级事务日志收集、流处理(如 Flink)
延迟/定时任务内置支持(如订单超时)需外部系统配合
大规模日志流适合中小规模超大规模日志场景(如大数据管道)
多协议支持兼容 JMS、MQTT 等仅原生协议

总结选择建议

  • RocketMQ:需要 业务级事务、延迟消息、多协议支持 的场景
  • Kafka:需要 超高吞吐、流处理集成、大规模日志 的场景

参考资料

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.7