AUTHORED_MARKDOWN / 2026-08-26
RabbitMQ 与 Kafka 学习笔记
从生产确认、队列持久化、消费者 ACK 到 RabbitMQ 与 Kafka 的场景差异,整理消息可靠性完整链路。
- 字符
- 10,132
- 标题节点
- 41
- 预计阅读
- 约 21 分钟
- 内容状态
- 持续整理
消息队列rabbitMQ 以及Kafka:
RabbitMQ 完整可靠性链路:
Producer
|
| 发送消息
↓
RabbitMQ
|
| Publisher Confirm
↑
Producer
RabbitMQ内部:
Durable Queue
+
Persistent Message
↓
Consumer
|
| 处理成功
↓
ACK
对应三层:
1. Producer → Broker
Publisher Confirm
2. Broker内部
Durable Queue
Persistent Message
3. Broker → Consumer
ACK / NACK
这就是 RabbitMQ 可靠性的主骨架。
Producer / Broker / Consumer:
MQ 的第一个核心价值:异步
第二个核心价值:削峰填谷
这是面试特别喜欢问的词。
假设你服务器:
每秒最多处理:
100个订单
但是双十一突然:
1秒来了10000个订单
如果直接:
10000请求
↓
数据库
数据库可能直接:
BOOM
加 RabbitMQ:
10000请求
↓
RabbitMQ
先排队:
Queue
10000
9999
9998
9997
...
消费者按照:
100/s
慢慢处理。
于是:
瞬时流量峰值
10000/s
↓
消息队列缓冲
↓
后台稳定处理
100/s
这个就叫:
削峰填谷
第三个核心价值:解耦
假设订单创建之后:
需要:
订单服务
↓
库存服务
↓
短信服务
↓
积分服务
↓
推荐服务
如果订单服务直接调用:
OrderService
├→ Inventory
├→ SMS
├→ Points
└→ Recommend
订单服务必须知道所有服务。
以后增加:
风控服务
订单服务还得改代码。
如果使用消息:
订单服务只做:
OrderCreated
发出去:
OrderService
↓
Message
"OrderCreated"
↓
MQ
然后:
库存系统 ← MQ
短信系统 ← MQ
积分系统 ← MQ
推荐系统 ← MQ
风控系统 ← MQ
订单服务根本不需要知道:
到底谁消费
这就叫:
服务解耦
第四个价值:提高可靠性
RabbitMQ 和 Kafka 为什么都会被叫 MQ?
因为它们都有:
Producer
↓
Broker
↓
Consumer
这个基本结构。
但是后面会发现:
RabbitMQ 更关注:
这条任务有没有被正确处理?
Kafka 更关注:
发生了什么事件?
消费者读到哪了?
历史事件还要不要重新读取?
所以这两个东西从第二层开始,就逐渐分叉了。
消息队列最核心解决四类问题:
Message Queue
│
┌─────────────┼
↓ ↓ ↓
异步 解耦 削峰
│
↓
可靠性
面试官问:
为什么要用消息队列?
你可以回答:
消息队列主要用于异步处理、服务解耦和流量削峰,同时可以结合消息确认、持久化、重试和死信机制提高任务处理可靠性。例如耗时 AI 推理任务可以由 API 服务只负责提交任务,RabbitMQ 负责缓冲和投递,再由后台 Worker 异步消费,避免长时间占用 Web 请求线程。
如果没有 Exchange,会发生什么?
假设系统里有三个消费者:
库存服务
邮件服务
积分服务
如果 Producer 直接往 Queue 发:
Producer
↓
Queue
↓
Consumer
那么 Producer 就必须知道:
库存队列叫什么?
邮件队列叫什么?
积分队列叫什么?
例如:
order-service
↓
inventory_queue
order-service
↓
email_queue
order-service
↓
points_queue
这时候订单服务和三个下游服务产生了强耦合。
RabbitMQ 的解决方案是:
Producer 不直接决定消息最终进入哪个 Queue(消息队列),而是先交给 Exchange(交换机,消息路由器)。
于是:
Producer
↓
Exchange
↓
根据规则分发
↓
Queue A / Queue B / Queue C
Producer 只负责:
“我产生了一条消息”
Exchange 负责:
“这条消息应该去哪里”
为什么 Exchange 非常重要?
因为它让发送消息和消息最终去哪完全分开。
Producer 只需要发给 order_exchange
不需要知道:
后面几个 Queue
几个 Consumer
它们叫什么
所以:
Producer
↓
Exchange
↙ ↘
Queue A Queue B
这就是解耦。
RabbitMQ 有哪些 Exchange?
这是 RabbitMQ 最核心的一组知识。
最常见四种:
Direct(精准匹配)
Fanout(广播)
Topic(模式匹配)
Headers
类型 核心逻辑 示例
Direct 精确匹配 "email"
Fanout 全部广播 所有绑定 Queue 都收到
Topic 模式匹配 order.*、order.#
RabbitMQ:
Producer
↓
Exchange
↓
Binding
↓
Queue
↓
Consumer
分别:
Producer
产生消息
Exchange
路由消息
Binding
规定路由关系
Queue
保存、排队消息
Consumer
处理消息
而 Exchange 三种最重要类型:
Direct
精确匹配
Fanout
广播
Topic
模式匹配
ACK 是什么?
ACK = Acknowledgement
中文一般叫:
确认应答 / 消息确认
核心意思:
Consumer 告诉 RabbitMQ:“这条消息我已经成功处理完了,你可以把它从 Queue 里真正删除了。”
流程:
Queue
↓
Message 1001
↓
Consumer
↓
处理成功
↓
ACK
↓
RabbitMQ 删除消息
所以要注意:
Consumer 收到消息 ≠ 消息已经处理成功。
Auto ACK 和 Manual ACK
RabbitMQ 有两种基本确认思路。
Auto ACK(自动确认) Manual ACK(手动确认)
Auto ACK问题:
Consumer收到
↓
RabbitMQ删除
↓
Consumer崩溃
可能消息丢失,所以涉及重要业务时,通常不建议这么干。
NACK = Negative Acknowledgement
可以理解为:
Consumer 主动告诉 RabbitMQ:“这条消息我没处理成功。”
涉及到NACK后是否需要重新入队的,通常涉及requeue = true / false;
NACK + requeue=true
意思:
我失败了,但请把消息重新放回 Queue。
NACK + requeue=false
意思:
这条消息处理不了,不要再放回来。
幂等性:
同一个操作执行一次和执行多次,最终业务结果一致。
例如:
消息:
{
"task_id": "9527"
}
Consumer 处理之前:
先查询 task_id=9527 是否已经完成
如果:
已完成
那么:
不重复执行
直接 ACK
RabbitMQ 如何保证消费者处理消息的可靠性?
可以回答:
RabbitMQ 可以使用手动 ACK。消息投递给 Consumer 后,在收到 ACK 前会处于 unacked 状态;如果 Consumer 在处理过程中异常退出或连接断开,RabbitMQ 会将未确认消息重新投递。处理失败时 Consumer 也可以通过 NACK 控制消息是否 requeue。由于重新投递可能产生重复消费,因此业务侧还需要设计幂等性,例如使用唯一业务 ID、数据库唯一约束或幂等记录。
可靠性:
Producer
↓
RabbitMQ
↓
Consumer
对应三个问题:
Producer → RabbitMQ
消息到底发成功没有?
RabbitMQ 自己
服务器重启后消息还在不在?
RabbitMQ → Consumer
消费者到底处理成功没有?
分别对应:
Publisher Confirm
Durable Queue + Persistent Message
ACK / NACK
完整的持久化通常需要两部分:
Durable Queue(队列持久化)
+
Persistent Message(消息持久化)
Publisher Confirm 是什么?
Publisher就是:
Producer(生产者)
Confirm就是:
确认(确认)
所以:Publisher Confirm = RabbitMQ 向 Producer 确认消息是否已经被 Broker 接收。
同时Publisher Confirm也是持久化以后就绝对不丢消息的关键所在。
Publisher Confirm与ACK:
Publisher Confirm=发送可靠性
Cnsumer ACK=消费可靠性
RabbitMQ 怎么保证消息不丢?
RabbitMQ 的可靠性要分三段处理。Producer 到 Broker 通过 Publisher Confirm 确认消息是否成功到达;Broker 侧使用 durable queue 和 persistent message,保证队列和消息在 Broker 重启后能够恢复;Consumer 侧使用手动 ACK/NACK,只有业务处理成功后才确认消息。如果消费者异常,未确认消息可以重新投递。实际业务还需要设计幂等性,因为重试和重新投递可能产生重复消息。
TTL 是什么?
TTL = Time To Live
意思:
消息最多允许存活多久。
RabbitMQ 官方支持队列级消息 TTL,也支持单条消息 TTL;过期消息不会再正常投递给消费者。
TTL 有两种常见形式
第一种:Queue 级 TTL
例如:
这个 Queue 里的所有消息
最多存活 30 秒
可以理解成:
retry_queue
所有进入这里的消息:
TTL = 30s
适合:
重试队列。
第二种:Message 级 TTL
不同消息可以有不同时间:
message A
TTL = 5s
message B
TTL = 60s
更灵活。
TTL 到期后消息去哪?
如果什么都不配置:
Message
↓
过期
↓
被删除
但实际项目经常不希望直接删除。
我们希望:
过期
↓
转发到另一个地方
这个地方就是:
DLX / DLQ
DLQ 是什么?
DLQ:
Dead Letter Queue
中文:
死信队列
所谓“死信”不是 RabbitMQ 出故障了。
而是这条消息已经不能按照正常流程继续处理了。
所以 DLQ 本质上是失败消息的隔离区。
DLX是什么?
DLX:
Dead Letter Exchange
是:
死信交换机。
一个完整 RabbitMQ 失败处理链路
你现在可以把系统画成:
Producer
↓
Exchange
↓
Main Queue
↓
Consumer
↓
处理
↓
成功?
/ \
是 否
↓ ↓
ACK 判断重试次数
↓
还能重试?
/ \
是 否
↓ ↓
Retry Queue DLQ
↓
TTL
↓
DLX
↓
Main Queue
RabbitMQ 消费失败一般怎么处理?
可以回答:
Consumer 一般使用手动 ACK。处理成功后 ACK,失败后根据错误类型决定是否重试。临时错误不会简单无限 requeue=true,因为可能形成毒消息循环,通常会通过 Retry Queue 配合 TTL 和 Dead Letter Exchange 实现延迟重试,并限制最大重试次数;超过次数或者属于永久性业务错误后进入 DLQ,便于告警、人工排查或后续重新投递。RabbitMQ 当前的 quorum queue 也支持 delivery limit 控制最大投递次数。
Prefetch 是什么?
Prefetch
直译可以理解成:
预取数量。
RabbitMQ 里的核心意思是:
一个 Consumer 在没有 ACK 之前,最多允许同时持有多少条未确认消息。
Prefetch没有标准,需要结合实际实际工作以及处理效率,它本质是在:公平性与吞吐量之间取平衡。
Q:一个 RabbitMQ Queue 可以有多个消费者吗?
可以。多个 Consumer 可以竞争消费同一个 Queue,消息通常只会投递给其中一个 Consumer,可以用这种方式做 Worker 横向扩容。
Q:Prefetch 是什么?
Prefetch 用来限制一个 Consumer 在未 ACK 前最多能持有多少条消息,从而避免 RabbitMQ 一次给某个消费者分配过多任务,提高负载分配公平性并控制内存压力。
Q:Prefetch 越大越好吗?
不是。大 Prefetch 可能提高吞吐量,但也会增加消息分配不均、内存占用和故障重投成本;小 Prefetch 更公平但可能降低吞吐量,需要根据任务耗时和 Consumer 并发能力调优。
Q:一个 Queue 后面 3 个 Consumer,一条消息会给 3 个人吗?
不会。共享同一个 Queue 的消费者是竞争消费,一条消息通常只由一个 Consumer 处理。如果希望多个业务都收到同一事件,应绑定多个 Queue 到 Exchange。
如果业务必须严格有序怎么办?
例如一个银行账户:
账户余额 = 100
事件:
1. +100
2. -50
3. +20
如果严格按照:
+100
-50
+20
最终:
170
但假如业务操作本身不满足交换律,而处理顺序被打乱,就可能造成状态异常。
对于这种要求严格 FIFO 的场景,一种典型思路是:
一个 Queue
+
Single Active Consumer
+
prefetch = 1
RabbitMQ 官方对需要非常严格顺序的场景也推荐 Single Active Consumer 等方案;其官方资料特别提到,要获得包括重投消息在内的更严格 FIFO,可以配合单活消费者与 prefetch=1。RabbitMQ
你目前不用深入 Single Active Consumer,只需要知道:
严格顺序通常意味着减少并发,而减少并发通常意味着牺牲吞吐。
这是分布式系统非常常见的 trade-off。
At-most-once 和 At-least-once
这里引入两个面试高频术语。
At-most-once
最多一次。
核心思想:
消息最多处理一次
可能:
0 次
或者
1 次
但不重复。
代价:
可能丢消息。
例如 Auto ACK:
RabbitMQ
↓
发给Consumer
↓
直接认为成功
↓
消息删除
Consumer 随后挂掉:
消息没处理
但也不会再发
这就是倾向:
At-most-once
9. At-least-once
至少一次。
消息至少处理一次
可能:
1 次
2 次
3 次
但尽量不丢。
典型:
Manual ACK
+
失败重投
这也是 RabbitMQ 可靠任务处理里更常见的思想。
代价:
必须接受“可能重复”。
幂等性到底是什么?
Idempotency:
同一个操作执行一次和执行很多次,最终业务结果相同。
为了保证业务幂等性,最常见两种方法**:1.在产出消息时(生产者层面)生成唯一业务ID;**
2.在数据库层面做好数据库唯一约束。
幂等性必须和具体业务事务边界一起设计。
PostgreSQL 通常更适合关键业务幂等
RabbitMQ 如何保证消息不重复消费?
RabbitMQ 在手动 ACK 和故障重投场景下通常提供至少一次投递语义,因此消息有可能重复消费。重复消费不能完全依赖 Broker 消除,需要业务侧设计幂等,例如使用唯一 message ID、数据库唯一约束、幂等表或状态机。如果业务处理和幂等记录都落在同一个数据库中,可以通过本地事务进一步保证一致性。
RabbitMQ 能保证顺序吗?
可以回答:
单 Queue 在简单情况下具有 FIFO 特性,但多个 Consumer 并发、消息重新入队、优先级等因素都可能导致最终处理顺序变化。如果业务需要严格顺序,可以限制为单活消费者并控制 prefetch,或者按照业务 key 拆分 Queue,但严格顺序通常会牺牲并发吞吐。
因此说RabbitMQ并不是“加一个中间件,系统就变好了。”而是用系统复杂度换取异步、解耦、削峰和可靠性。
RabbitMQ 消息堆积怎么处理?
回答:
首先需要判断堆积发生在哪个阶段。如果 Ready 数量持续增加,通常说明 Producer 生产速度超过 Consumer 消费能力,需要增加消费者、优化消费逻辑或者限制生产速度。如果 Unacked 数量增加,说明消费者已经获取消息但没有完成确认,需要检查消费者状态和业务异常。同时需要关注下游依赖,比如数据库、第三方 API 或模型服务是否成为瓶颈。生产环境还需要设置合理的 Prefetch、监控队列长度,并设计限流和扩容策略。
RabbitMQ 集群
Broker就是一个运行 RabbitMQ 服务的节点,由多个RabbitMQ Server共同组成一个消息系统。
用rabbitmq集群本质就与redis的集群,哨兵一致,需要集群以及多Broker,具体原因有三:
原因1:高可用,如果一个挂了会有其他broker提供服务;
原因2:容量扩展,多个rabbitMQ协作可以提高整体性能,最大消息处理条数,满足单个rabbitMQ完成不了的性能需要;
原因3:隔离故障,例如:一个业务的订单系统流量突然暴涨。
如果所有业务都共用一个节点,就容易导致订单爆炸,从而让其他业务一起受影响。而集群可以做隔离。
RabbitMQ 集群是不是简单复制所有消息?
不是。
默认情况下,RabbitMQ 集群主要共享:
元数据
Exchange
Queue 定义
用户权限
但是:
消息本身如何复制,需要具体队列类型决定。
镜像队列是什么?
思想:
一个 Queue:
task_queue
复制到:
Node1
Node2
Node3
结构:
task_queue
/ | \
Node1 Node2 Node3
主 副 副
Node1:
Leader
Node2/3:
Replica
如果:
Node1挂掉
可以:
Node2成为主节点
继续服务。
为什么后来不用镜像队列?
问题1:同步成本高
问题2:一致性复杂
问题3:大规模场景性能下降
节点越多,复制压力越大。
Quorum Queue 是什么?
Quorum:法定人数 / 多数派。
它是一种基于:Raft 共识算法实现的队列。
其核心思想为多个节点保存副本,并通过多数节点确认状态。在这种情况下,只要过一半节点存活,
系统就还能继续工作。
同时Quorum Queue 和 Kafka Partition Leader 很像,
RabbitMQ 数据持久化和高可用区别:
持久化
解决:
RabbitMQ 重启,消息还在吗?
高可用
解决:
RabbitMQ 服务器坏了怎么办?
RabbitMQ 如何保证高可用?
回答:
RabbitMQ 可以通过集群部署提高 Broker 层面的可用性,并通过 Quorum Queue 实现队列数据的复制。Quorum Queue 基于 Raft 共识算法,通过多数节点确认保证一致性。当 Leader 节点故障时,可以选举新的 Leader 继续提供服务。同时消息持久化需要结合 Durable Queue 和 Persistent Message,两者分别解决重启恢复和节点故障问题。
RabbitMQ 集群中所有节点都有所有消息吗?
回答:
不一定。RabbitMQ 集群默认主要同步元数据,消息是否复制取决于队列类型。Classic Queue 默认不是多副本,Quorum Queue 会在多个节点之间复制消息,因此生产环境通常推荐 Quorum Queue 来实现高可靠消息存储。
RabbitMQ 到底解决什么问题?
现在换一个高度。
RabbitMQ 本质:
不是数据库。
不是缓存。
它解决:
服务之间可靠异步通信。
Quorum Queue 为什么比镜像队列更推荐?
回答:
Quorum Queue 基于 Raft 共识算法,通过多数派确认保证数据一致性,相比传统镜像队列,在一致性、故障恢复和大规模场景下更可靠,是 RabbitMQ 4.x 推荐的高可靠队列方案。
RabbitMQ 和 Kafka 有什么区别?
回答:
RabbitMQ 和 Kafka 都是消息系统,但设计目标不同。RabbitMQ 更偏向传统消息队列,关注消息可靠投递、任务分发和业务流程控制,通过 Exchange、Queue、ACK、Retry 等机制保证任务处理可靠性。Kafka 更偏向事件流平台,通过 Topic、Partition 和 Offset 实现高吞吐的数据流处理,消息可以长期保存并被多个消费者重复消费。因此异步任务、业务解耦通常选择 RabbitMQ,而日志采集、数据分析、事件驱动架构通常选择 Kafka,实际大型系统中两者可能同时存在。
RabbitMQ
像:
任务清单
拿走任务:
完成
删除
Kafka
像:
日志文件
记录发生过什么。
谁需要:
谁读取。