vault backup: 2026-05-24 20:51:06
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
---
|
||||
tags:
|
||||
- MQ
|
||||
create time: 2026-05-24 19:52
|
||||
---
|
||||
|
||||
# MQ 基础概念
|
||||
|
||||
## 概述
|
||||
|
||||
消息队列(Message Queue,简称 MQ)是一种异步通信机制,允许应用程序通过消息的生产和消费来实现解耦与协作。本文从生活类比出发,梳理核心术语、基本工作流程以及 MQ 的三大核心价值。
|
||||
|
||||
## 正文
|
||||
|
||||
### 什么是消息队列
|
||||
|
||||
想象一下你去寄快递:你把包裹交给快递柜,快递柜暂存包裹,收件人有空时再来取件。你不需要知道收件人什么时候在家,快递柜也不关心包裹里装的是什么——三方各司其职。
|
||||
|
||||
消息队列做的事情完全一样:**Producer(生产者)把消息投递到 Broker(消息服务器),Broker 暂存消息,Consumer(消费者)按自己的节奏拉取并处理。** 生产和消费在时间上解耦,双方不需要同时在线。
|
||||
|
||||
> [!question]
|
||||
> 生活中还有哪些场景天然就是"消息队列"的模式?食堂排队打饭算不算?
|
||||
|
||||
### 核心术语图解
|
||||
|
||||
| 术语 | 说明 | 类比 |
|
||||
|------|------|------|
|
||||
| **Producer** | 消息的发送方 | 寄件人 |
|
||||
| **Broker** | 消息服务器,负责存储和转发 | 快递柜 / 邮局 |
|
||||
| **Consumer** | 消息的接收方和处理方 | 收件人 |
|
||||
| **Topic** | 消息的逻辑分类,Producer 按 Topic 发送 | 快递柜的格口编号 |
|
||||
| **Queue** | 消息的物理存储单元 | 实际的格口 |
|
||||
| **Message** | 传输的数据单元 | 包裹 |
|
||||
| **Offset** | 消息在队列中的位置标识,用于消费进度追踪 | 快递单号 |
|
||||
| **Partition** | Topic 的物理分片,实现并行消费 | 同一排快递柜的不同列 |
|
||||
|
||||
### 基本工作流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant P as "Producer"
|
||||
participant B as "Broker"
|
||||
participant C as "Consumer"
|
||||
|
||||
P->>B: 1. 发送消息到 Topic
|
||||
B->>B: 2. 持久化消息,分配 Offset
|
||||
C->>B: 3. 拉取消息(Pull)或接收推送(Push)
|
||||
B-->>C: 4. 返回消息
|
||||
C->>C: 5. 处理业务逻辑
|
||||
C->>B: 6. 提交消费确认(Ack)
|
||||
```
|
||||
|
||||
> [!question]
|
||||
> 第 6 步的 Ack 确认机制为什么重要?如果没有 Ack 会怎样?
|
||||
|
||||
### MQ 的三大核心价值
|
||||
|
||||
#### 1. 解耦
|
||||
|
||||
没有 MQ 时,下单服务需要直接调用库存、积分、物流等多个下游服务,形成蜘蛛网般的依赖。引入 MQ 后,下单服务只需发一条消息,各下游自行订阅。
|
||||
|
||||
```go
|
||||
// 解耦前: 下单服务直接依赖所有下游
|
||||
func CreateOrder(order Order) {
|
||||
db.Save(order)
|
||||
inventorySvc.Deduct(order.ItemID, order.Qty) // 强依赖
|
||||
pointsSvc.Add(order.UserID, order.Points) // 强依赖
|
||||
logisticsSvc.Create(order) // 强依赖
|
||||
// 新增通知服务?改代码、重新部署...
|
||||
}
|
||||
|
||||
// 解耦后: 只管发消息,下游自己订阅
|
||||
func CreateOrder(order Order) {
|
||||
db.Save(order)
|
||||
mq.Publish("order_created", order) // 一条消息,谁关心谁订阅
|
||||
}
|
||||
```
|
||||
|
||||
解耦的本质是把**"我知道谁需要"变成"谁需要谁知道"**,新增下游服务时生产者代码零改动。
|
||||
|
||||
#### 2. 异步
|
||||
|
||||
有些操作不需要用户等待结果。比如注册后发欢迎邮件、生成报表等,可以把这些耗时操作异步化。
|
||||
|
||||
```go
|
||||
// 同步: 用户等所有操作完成,响应慢
|
||||
func Register(user User) {
|
||||
db.Save(user) // 50ms
|
||||
emailSvc.SendWelcome(user) // 200ms
|
||||
analyticsSvc.Track(user) // 100ms
|
||||
// 总耗时 350ms,用户等到怀疑人生
|
||||
}
|
||||
|
||||
// 异步: 用户只等核心操作,其余扔给 MQ
|
||||
func Register(user User) {
|
||||
db.Save(user) // 50ms
|
||||
mq.Publish("user_registered", user) // 5ms
|
||||
// 总耗时 55ms,邮件和埋点慢慢处理
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 削峰
|
||||
|
||||
秒杀场景下,瞬间流量可能打垮数据库。MQ 充当缓冲区,让下游按自己的处理能力消费消息。
|
||||
|
||||
```go
|
||||
// 削峰前: 瞬时 1 万 QPS 直接打到数据库,DB 直接跪
|
||||
func Seckill(userID, itemID string) {
|
||||
if stock > 0 {
|
||||
db.UpdateStock(itemID, -1) // DB 瞬间被打爆
|
||||
}
|
||||
}
|
||||
|
||||
// 削峰后: 先接住流量,慢慢消费
|
||||
func Seckill(userID, itemID string) {
|
||||
mq.Publish("seckill_request", SeckillReq{userID, itemID}) // 瞬时接住
|
||||
}
|
||||
|
||||
// 消费者按 DB 承受能力匀速消费
|
||||
func ConsumeSeckill() {
|
||||
for msg := range mq.Subscribe("seckill_request") {
|
||||
if stock > 0 {
|
||||
db.UpdateStock(msg.ItemID, -1) // 匀速处理,DB 稳如老狗
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### MQ vs 直接调用
|
||||
|
||||
| 维度 | 直接调用 | 消息队列 |
|
||||
|------|---------|---------|
|
||||
| 耦合度 | 强耦合,调用方需感知下游 | 松耦合,生产者不关心消费者 |
|
||||
| 响应时间 | 等待所有下游完成 | 只等核心链路,非核心异步处理 |
|
||||
| 可靠性 | 下游挂了直接失败 | Broker 持久化,支持重试 |
|
||||
| 吞吐能力 | 受限于最慢的下游 | 通过缓冲削峰,提升整体吞吐 |
|
||||
| 复杂度 | 简单直观 | 引入额外组件,运维成本上升 |
|
||||
| 一致性 | 容易保证强一致性 | 最终一致性,需要额外设计 |
|
||||
|
||||
> [!question]
|
||||
> 如果不需要解耦和削峰,还有必要引入 MQ 吗?
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[2-MQ-适用场景与选型原则]]
|
||||
- [[3-MQ-消息模型]]
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
tags:
|
||||
- MQ
|
||||
create time: 2026-05-24 19:52
|
||||
---
|
||||
|
||||
# MQ 适用场景与选型原则
|
||||
|
||||
## 概述
|
||||
|
||||
MQ 并非银弹。本文详解三大经典适用场景的架构实践,明确不适合使用 MQ 的边界,并给出选型决策框架和常见反模式警示。
|
||||
|
||||
## 正文
|
||||
|
||||
### 三大经典场景详解
|
||||
|
||||
#### 场景一:系统解耦
|
||||
|
||||
当多个下游系统需要感知同一事件时,MQ 让生产者无需感知消费者的存在。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
O["Order Service"] -->|Publish| MQ["MQ Broker"]
|
||||
MQ -->|Subscribe| INV["Inventory Service"]
|
||||
MQ -->|Subscribe| PTS["Points Service"]
|
||||
MQ -->|Subscribe| LOG["Logistics Service"]
|
||||
MQ -->|Subscribe| NOTIFY["Notification Service"]
|
||||
```
|
||||
|
||||
新增"数据仓库"服务?只需要订阅 `order_created` topic,Order Service 的代码一行不用动。
|
||||
|
||||
```go
|
||||
// Order Service 只管发消息
|
||||
func CreateOrder(order Order) {
|
||||
db.Save(order)
|
||||
mq.Publish("order_created", order)
|
||||
}
|
||||
|
||||
// Inventory Service 独立订阅
|
||||
func HandleOrderCreated(msg Message) {
|
||||
var order Order
|
||||
json.Unmarshal(msg.Body, &order)
|
||||
inventory.Deduct(order.ItemID, order.Qty)
|
||||
}
|
||||
```
|
||||
|
||||
#### 场景二:异步处理
|
||||
|
||||
将非核心链路从主流程中剥离,缩短用户感知的响应时间。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
U["User Request"] --> API["API Gateway"]
|
||||
API --> DB["Core DB Write"]
|
||||
API -->|Async| MQ["MQ Broker"]
|
||||
MQ --> EMAIL["Email Service"]
|
||||
MQ --> ANALYTICS["Analytics Service"]
|
||||
MQ --> SEARCH["Search Index Update"]
|
||||
```
|
||||
|
||||
以电商下单为例,核心链路是扣库存和创建订单记录(同步),而发邮件、更新搜索索引、埋点统计等可以异步处理。
|
||||
|
||||
```go
|
||||
func PlaceOrder(order Order) {
|
||||
// 同步: 核心链路
|
||||
db.CreateOrder(order)
|
||||
db.DeductStock(order.ItemID, order.Qty)
|
||||
|
||||
// 异步: 非核心链路,扔给 MQ
|
||||
mq.Publish("order_placed", order) // 邮件、搜索索引、埋点各自消费
|
||||
}
|
||||
```
|
||||
|
||||
> [!question]
|
||||
> 如果异步任务失败了怎么办?比如邮件发送失败,用户收不到通知——你需要什么机制来保证最终成功?
|
||||
|
||||
#### 场景三:流量削峰
|
||||
|
||||
秒杀、抢购等场景下,瞬时流量远超系统承载能力。MQ 作为缓冲层,让下游匀速处理。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
REQ["10000 QPS"] --> LB["Load Balancer"]
|
||||
LB --> APP["App Server"]
|
||||
APP -->|Instant peak| MQ["MQ Buffer"]
|
||||
MQ -->|Rate limited| DB["Database"]
|
||||
```
|
||||
|
||||
```go
|
||||
// 生产者: 接住流量
|
||||
func Seckill(ctx context.Context, req SeckillReq) error {
|
||||
// 先做前置校验(限流、参数校验)
|
||||
if !rateLimiter.Allow() {
|
||||
return ErrTooManyRequests
|
||||
}
|
||||
return mq.Publish("seckill", req)
|
||||
}
|
||||
|
||||
// 消费者: 匀速消费,保护数据库
|
||||
func SeckillConsumer() {
|
||||
for msg := range mq.Subscribe("seckill") {
|
||||
// 限速: 每秒最多处理 500 个请求
|
||||
rateLimiter.Wait()
|
||||
processSeckill(msg)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> [!question]
|
||||
> 削峰场景中,如果 MQ 的消费速度持续低于生产速度,会发生什么?如何应对?
|
||||
|
||||
### 不适合使用 MQ 的场景
|
||||
|
||||
MQ 解决了分布式系统中的很多问题,但有些场景引入 MQ 反而画蛇添足:
|
||||
|
||||
**1. 强一致性要求**
|
||||
|
||||
金融转账、扣款等场景要求操作原子性。MQ 的异步特性天然支持最终一致性,但无法保证强一致。如果 A 扣款成功但消息丢失,B 没收到钱——这就是事故。
|
||||
|
||||
**2. 简单的 RPC 调用**
|
||||
|
||||
如果 A 服务调用 B 服务,只需要一个同步返回值(比如查询用户信息),直接 HTTP/gRPC 调用更简单。引入 MQ 多了一层 Broker,增加了延迟和运维成本,纯属多此一举。
|
||||
|
||||
**3. 实时性极高的场景**
|
||||
|
||||
游戏服务器的实时状态同步、在线聊天等场景要求毫秒级响应。MQ 的存储转发机制会引入额外延迟,直接 WebSocket 或长连接更合适。
|
||||
|
||||
### 选型决策框架
|
||||
|
||||
选型时需要综合考虑以下维度,没有"最好"的 MQ,只有"最合适"的:
|
||||
|
||||
| 维度 | Kafka | RabbitMQ | RocketMQ | Pulsar |
|
||||
|------|-------|----------|----------|--------|
|
||||
| 吞吐量 | 极高(百万级/s) | 中等(万级/s) | 高(十万级/s) | 极高(百万级/s) |
|
||||
| 延迟 | 毫秒~秒级 | 微秒~毫秒级 | 毫秒级 | 毫秒级 |
|
||||
| 消息可靠性 | 高(副本机制) | 高(镜像队列) | 高(同步双写) | 高(BookKeeper) |
|
||||
| 运维成本 | 中等(依赖 ZK/KRaft) | 低(开箱即用) | 中等 | 高(组件多) |
|
||||
| 生态 | 极丰富(Flink/Spark) | 成熟(插件体系) | 较丰富 | 成长中 |
|
||||
| 典型场景 | 大数据管道、日志 | 企业应用、复杂路由 | 电商、金融 | 多租户、云原生 |
|
||||
|
||||
> [!question]
|
||||
> 如果你的团队只有 3 个人,系统日均消息量 10 万条,你会选择哪个 MQ?为什么?
|
||||
|
||||
### 反模式警示
|
||||
|
||||
**MQ 万能论**
|
||||
|
||||
"所有异步操作都用 MQ"是一个常见误区。如果只是简单的后台任务,用一个 goroutine 池或者任务调度器就够了,不需要引入一个分布式 Broker。MQ 的运维成本、消息丢失风险、幂等性处理等都是实打实的工程代价。
|
||||
|
||||
**过度异步化**
|
||||
|
||||
把本该同步完成的核心链路也异步化,会导致:
|
||||
- 事务边界变得模糊,难以保证数据一致性
|
||||
- 排查问题时调用链断裂,日志追踪困难
|
||||
- 用户反馈"下单成功了但库存没扣"——因为扣库存的消息还在队列里排队
|
||||
|
||||
记住一个原则:**用户需要立即看到结果的操作,不要异步化。**
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[1-MQ-基础概念]]
|
||||
- [[3-MQ-消息模型]]
|
||||
Reference in New Issue
Block a user