02 - 核心概念与工作模型
上一章我们用”餐厅挂单栏”建立了直觉。这一章把这套模型正式拆开,认识几个所有消息队列都通用的核心角色。搞懂了它们,你换到任何 MQ 框架都能秒懂。
2.1 四个核心角色
┌──────────┐ ┌──────────────────────────┐ ┌──────────┐
│ 生产者 │─────▶│ Broker │─────▶│ 消费者 │
│ Producer │ 发消息│ ┌────────────────────┐ │ 取消息│ Consumer │
│ │ │ │ 队列 Queue │ │ │ │
│(FastAPI) │ │ │ [msg][msg][msg]... │ │ │(后台worker)│
└──────────┘ │ └────────────────────┘ │ └──────────┘
│ (消息在这里排队暂存) │
└──────────────────────────┘
| 角色 | 对应餐厅 | 职责 |
|---|---|---|
| 生产者 Producer | 点单的服务员 | 产生消息、把消息发给 Broker。通常就是你的 FastAPI 接口 |
| 消费者 Consumer | 后厨的厨师 | 从 Broker 取出消息并处理。通常是独立运行的后台 worker |
| Broker(消息中间件) | 挂单栏所在的整个后厨系统 | 独立运行的服务,负责接收、暂存、投递消息。RabbitMQ / Kafka 等就是 Broker |
| 消息 Message | 一张小票 | 被传递的数据本身,比如 {"event": "user_registered", "user_id": 1001} |
关键认知:Broker 是一个独立的服务进程,通常单独部署(甚至单独一台机器 / 一个 Docker 容器)。你的 FastAPI 应用和后台 worker 都通过网络连接到它。它们三者是分开的。
2.2 消息的一生:从产生到被处理
一条消息完整走一遍,是这样的:
① 生产者创建消息
{"event": "user_registered", "user_id": 1001}
│
▼ 通过网络发送
② Broker 收下消息,放进队列排队
队列: [msg3][msg2][msg1] ← 新消息进这头
│
▼
③ 消费者向 Broker 说"给我来一条"
│
▼
④ Broker 把队头的消息投递给消费者
│
▼
⑤ 消费者处理消息(发邮件 / 写库 / ...)
│
▼
⑥ 处理成功后,消费者告诉 Broker:"这条我搞定了"(确认 ACK)
│
▼
⑦ Broker 收到确认,把这条消息从队列里删掉
其中第 ⑥ 步的确认机制(ACK)非常关键,它是消息队列”不丢消息”的核心保障——如果消费者处理到一半崩了、没发确认,Broker 会认为这条消息没处理成功,之后会重新投递给别的消费者。我们在 第 07 章 会专门讲。
现在你只要记住一句话:消息不是”发出去就删”,而是”确认处理成功后才删”。 这就是它比”直接调用”更可靠的原因。
2.3 两种最基本的消息模型
不同场景下,“消息该发给谁”是不一样的。这引出两种最经典的模型。
模型一:点对点(一条消息只被一个消费者处理)
一个队列,多个消费者抢着消费,但每条消息只会被其中一个消费者拿到。适合”任务分发”:比如 100 个发邮件任务,交给 3 个 worker 分摊干活。
┌──────────┐
┌───▶│ 消费者 A │ 处理 msg1、msg4 …
┌──────────┐ │ └──────────┘
│ 队列 │────┤ ┌──────────┐
│[m4][m3][m2]│ ├───▶│ 消费者 B │ 处理 msg2、msg5 …
│[m1] │ │ └──────────┘
└──────────┘ │ ┌──────────┐
└───▶│ 消费者 C │ 处理 msg3、msg6 …
└──────────┘
一条消息只发给 A / B / C 中的一个 → 多个 worker 分摊工作(负载均衡)
好处:加机器就能提速。任务处理不过来?多起几个消费者进程就行,它们会自动分摊队列里的消息。
模型二:发布/订阅(一条消息被所有订阅者各收一份)
一条消息,每个关心它的消费者都能收到一份自己的副本。适合”一个事件,多方响应”:比如”用户已注册”这个事件,邮件服务、短信服务、推荐系统都想知道。
┌────────────┐
┌────▶│ 邮件服务 │ 收到一份"用户已注册"
┌──────────┐ │ └────────────┘
│ "用户已注册"│─┼────▶┌────────────┐
│ 事件 │ │ │ 短信服务 │ 收到一份"用户已注册"
└──────────┘ │ └────────────┘
└────▶┌────────────┐
│ 推荐系统 │ 收到一份"用户已注册"
└────────────┘
同一条消息,每个订阅者各拿一份 → 一次广播,多方响应
RabbitMQ 通过一个叫交换机(Exchange)的部件来实现这两种模型的灵活切换,这是它设计上最有特色的地方,第 05 章 细讲。你现在只要理解这两种模型的区别:一条消息是”被抢走一次”,还是”人手一份”。
2.4 为什么要有 Broker 这个”中间人”?
你可能会想:生产者直接调用消费者不行吗?为什么非要中间夹一个 Broker?
因为有了这个中间人,生产者和消费者就实现了”三个不用”:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 生产者 │ ──X──▶ │ Broker │ ──X──▶ │ 消费者 │
└──────────┘ └──────────┘ └──────────┘
① 不用同时在线:消费者哪怕暂时挂了,消息也在 Broker 里存着,等它恢复再处理
② 不用互相认识:生产者只管发给 Broker,根本不需要知道有几个消费者、它们在哪
③ 不用同步等待:生产者发完就走,不用站着等消费者处理完
这”三个不用”,正是 第 03 章 里”解耦 / 异步 / 削峰”的底层原理。
本章小结
| 知识点 | 要点 |
|---|---|
| 四个角色 | 生产者、消费者、Broker(中间件)、消息 |
| Broker 是什么 | 独立运行的服务,负责接收、暂存、投递消息 |
| 消息的一生 | 产生 → 入队 → 投递 → 处理 → 确认 ACK → 删除 |
| ACK 的意义 | 消息”确认成功后才删”,处理失败会重投,这是不丢消息的关键 |
| 两种模型 | 点对点(一条消息一个消费者,用于任务分摊)/ 发布订阅(一条消息人手一份,用于事件广播) |
| 为什么要 Broker | 让收发双方”不用同时在线、不用互相认识、不用同步等待” |
下一章预告:把这些概念串成价值——消息队列到底怎么实现解耦、异步、削峰这三大目标。
上一章 ← 01 - 为什么需要消息队列 | 下一章 → 03 - 解决的三大问题