个人学习笔记MESSAGE QUEUE

AUTHORED_MARKDOWN / 2026-08-26

RabbitMQ 与 Kafka 学习笔记

从生产确认、队列持久化、消费者 ACK 到 RabbitMQ 与 Kafka 的场景差异,整理消息可靠性完整链路。

.MD
字符
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

像:

日志文件

记录发生过什么。

谁需要:

谁读取。