--- 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-消息模型]]