05 - RabbitMQ 的架构与工作原理
选定了 RabbitMQ,这一章带你看它内部到底怎么转。重点理解它最有特色的设计——交换机(Exchange),这也是它区别于其他 MQ 的灵魂。
记住一句话:在 RabbitMQ 里,生产者从不直接把消息发给队列,而是发给”交换机”,由交换机决定消息该进哪个(些)队列。
5.1 RabbitMQ 的核心部件
┌──────────┐ 发消息 ┌─────────────────────────────────────┐ ┌──────────┐
│ 生产者 │──────────▶│ RabbitMQ Broker │──▶│ 消费者 │
│ Producer │ (带路由键) │ │ │ Consumer │
└──────────┘ │ ┌──────────┐ 绑定 ┌──────────┐ │ └──────────┘
│ │ 交换机 │───────▶│ 队列 Q1 │ │
│ │ Exchange │───────▶│ 队列 Q2 │ │
│ └──────────┘ 绑定 └──────────┘ │
│ ▲ │
│ 按"路由规则"决定进哪个队列 │
└─────────────────────────────────────┘
| 部件 | 作用 |
|---|---|
| Exchange(交换机) | 消息的”分拣中心”。接收生产者的消息,按规则决定转发到哪些队列。生产者只认识它 |
| Queue(队列) | 消息真正排队暂存的地方,消费者从这里取消息 |
| Binding(绑定) | 交换机和队列之间的”连线规则”,告诉交换机”满足什么条件的消息该送到我这个队列” |
| Routing Key(路由键) | 生产者给消息贴的”地址标签”,交换机拿它去匹配绑定规则 |
用邮局来类比整套流程:
你寄信(生产者)
→ 信封上写收件地址(路由键)
→ 交到邮局分拣中心(交换机)
→ 分拣中心按地址规则(绑定)把信投进对应的信箱(队列)
→ 收件人(消费者)从自己的信箱取信
关键点:生产者只把信交给”分拣中心”,它不需要知道有几个信箱、信最终进了哪个。这就是 RabbitMQ 灵活和解耦的来源。
5.2 交换机的四种类型(RabbitMQ 的灵魂)
交换机怎么”分拣”,取决于它的类型。理解这四种类型,就理解了 RabbitMQ 的路由能力。
① Direct(直连)——精确匹配
路由键完全相等才转发。适合”按类型精确派发”。
路由键 = "email"
生产者 ──(routing_key=email)──▶ ┌─────────┐ ──绑定 email──▶ [邮件队列]
│ Direct │
│ 交换机 │ ──绑定 sms ──▶ [短信队列]
└─────────┘
消息路由键是 email → 只进"邮件队列";是 sms → 只进"短信队列"
② Fanout(扇出)——广播给所有
忽略路由键,把消息复制一份发给所有绑定的队列。这就是 第 02 章 说的”发布/订阅”模型。
┌─────────┐ ──▶ [邮件队列]
生产者 ──(消息)──────────▶│ Fanout │ ──▶ [短信队列]
│ 交换机 │ ──▶ [推荐队列]
└─────────┘ ──▶ [统计队列]
一条"用户已注册" → 所有队列人手一份(一次广播,多方响应)
③ Topic(主题)——按模式通配匹配
路由键按 . 分段,用 *(匹配一段)和 #(匹配多段)做模糊匹配。最灵活,适合复杂的分类订阅。
生产者发 routing_key = "order.cn.vip"
┌─────────┐
│ Topic │ ──绑定 order.# ──▶ [所有订单队列] ✅命中
│ 交换机 │ ──绑定 *.cn.* ──▶ [中国区队列] ✅命中
└─────────┘ ──绑定 user.# ──▶ [用户事件队列] ✗不命中
一条消息可同时命中多个规则,进多个队列
④ Headers(头匹配)——按消息属性匹配
按消息的自定义属性(headers)而非路由键匹配。用得较少,知道有这么个东西即可。
小结:四种类型怎么记
| 类型 | 匹配方式 | 一句话场景 |
|---|---|---|
| Direct | 路由键精确相等 | 按明确类型派发(这条给邮件、那条给短信) |
| Fanout | 无视路由键,全广播 | 一个事件通知所有人(发布/订阅) |
| Topic | 路由键通配匹配 | 灵活的按主题订阅(最常用、最强大) |
| Headers | 按消息头属性 | 特殊需求,少用 |
实际项目里,Direct 和 Topic 用得最多,Fanout 用于广播。搞懂前三个就够应付绝大部分场景了。
5.3 一条消息在 RabbitMQ 里的完整旅程
把前面的部件串起来,走一遍完整流程:
① 生产者(FastAPI) 连接 Broker,发送消息
消息内容: {"user_id": 1001} 路由键: "user.registered"
│
▼
② 消息到达【交换机】(比如一个 Topic 交换机)
│
▼
③ 交换机根据【绑定规则】匹配路由键,决定投给哪些【队列】
匹配到: 邮件队列(绑定 user.#)、统计队列(绑定 user.registered)
│
├──────────────┬──────────────┐
▼ ▼
[邮件队列] [统计队列] (消息各进一份,在队列里排队)
│ │
▼ ▼
④ 各自的【消费者】从队列取出消息处理
邮件worker发欢迎邮件 统计worker更新数据
│ │
▼ ▼
⑤ 处理成功后发【确认 ACK】,Broker 才把消息从队列删除
5.4 补充两个概念:Connection 与 Channel
实际连接 RabbitMQ 时会遇到这两个词,简单理解即可:
┌────────────────────────────────────────┐
│ 一个 Connection(TCP 连接) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │Channel 1│ │Channel 2│ │Channel 3│ │
│ └─────────┘ └─────────┘ └─────────┘ │
└────────────────────────────────────────┘
应用 ←──── 网络 ────→ RabbitMQ Broker
- Connection:应用和 Broker 之间的一条 TCP 网络连接,建立成本较高。
- Channel(信道):在一条 Connection 上开的逻辑通道,真正的收发消息都在 Channel 上进行。
为什么要 Channel?因为建 TCP 连接很贵。一个应用建一条 Connection,然后在上面开多个轻量的 Channel 复用,既省资源又能并发。你只要记住:一条连接可以开很多信道,实际干活的是信道。
5.5 部署形态:Broker 是独立服务
再强调一次架构上的关键认知:RabbitMQ Broker 是一个独立运行的服务,通常用 Docker 单独跑一个容器。你的 FastAPI 和消费者都通过网络(默认端口 5672)连它。它还自带一个 Web 管理后台(默认端口 15672),能可视化地看队列、消息堆积情况。
┌──────────────┐ ┌────────────────────┐ ┌──────────────┐
│ FastAPI 容器 │─5672──▶│ RabbitMQ 容器 │◀─5672──│ 消费者 容器 │
│ (生产者) │ │ 管理后台 :15672 │ │ (worker) │
└──────────────┘ └────────────────────┘ └──────────────┘
三个独立服务,通过网络协作(正好用你学过的 Docker 编排)
本章小结
| 知识点 | 要点 |
|---|---|
| 核心设计 | 生产者发给交换机,交换机按规则路由到队列,不直连队列 |
| 四大部件 | Exchange(分拣中心)、Queue(排队处)、Binding(连线规则)、Routing Key(地址标签) |
| 交换机类型 | Direct(精确)、Fanout(广播)、Topic(通配,最常用)、Headers(少用) |
| Connection/Channel | 一条 TCP 连接上开多个轻量信道复用,实际收发在信道上 |
| 部署 | Broker 是独立服务(端口 5672),自带 Web 管理后台(15672),适合用 Docker 跑 |
下一章预告:把 RabbitMQ 接到你的 FastAPI 项目里——从架构视角看生产者、消费者、Broker 怎么分工部署,消息怎么流动。
上一章 ← 04 - 选型 | 下一章 → 06 - FastAPI + RabbitMQ 的整合架构思路