Files
cs-note/hhs/MQ/01-基础概念/2-MQ-适用场景与选型原则.md
T
2026-05-24 20:51:06 +08:00

163 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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-消息模型]]