vault backup: 2026-05-24 20:58:10
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
---
|
||||
tags:
|
||||
- MQ
|
||||
- 基础概念
|
||||
- 异步
|
||||
- 架构
|
||||
create time: 2026-05-24 19:52
|
||||
---
|
||||
|
||||
@@ -8,7 +11,7 @@ create time: 2026-05-24 19:52
|
||||
|
||||
## 概述
|
||||
|
||||
消息队列(Message Queue,简称 MQ)是一种异步通信机制,允许应用程序通过消息的生产和消费来实现解耦与协作。本文从生活类比出发,梳理核心术语、基本工作流程以及 MQ 的三大核心价值。
|
||||
消息队列(Message Queue,简称 MQ)是一种异步通信机制,允许应用程序通过消息的生产和消费来实现解耦与协作。本文从生活类比出发,梳理核心术语、Pull/Push 两种消费模型、基本工作流程、MQ 的三大核心价值,以及引入 MQ 需要付出的代价。
|
||||
|
||||
## 正文
|
||||
|
||||
@@ -53,6 +56,22 @@ sequenceDiagram
|
||||
> [!question]
|
||||
> 第 6 步的 Ack 确认机制为什么重要?如果没有 Ack 会怎样?
|
||||
|
||||
### Pull vs Push:两种消费模型
|
||||
|
||||
流程图第 3 步提到了"拉取"和"推送"两种方式,这其实是消息消费的两种根本模型:
|
||||
|
||||
| 维度 | **Pull(拉取)** | **Push(推送)** |
|
||||
|------|-----------------|-----------------|
|
||||
| 主动方 | Consumer 主动拉取 | Broker 主动推送 |
|
||||
| 消费节奏 | Consumer 自己控制,想拉多少拉多少 | Broker 控制推送速率,Consumer 被动接收 |
|
||||
| 适用场景 | 消费能力不均匀、需要精确控速 | 低延迟要求、实时性敏感 |
|
||||
| 典型代表 | Kafka、RocketMQ(默认) | RabbitMQ、MQTT |
|
||||
|
||||
**简单理解**:Pull 是"我去超市买东西"(我决定什么时候买、买多少),Push 是"外卖送到家门口"(商家决定什么时候送)。
|
||||
|
||||
> [!tip]
|
||||
> 大多数现代 MQ 默认采用 Pull 模式,因为 Consumer 更清楚自己的处理能力,天然具备背压(Backpressure)能力。详细的流控与背压机制见 [[31-MQ-背压与流控]]。
|
||||
|
||||
### MQ 的三大核心价值
|
||||
|
||||
#### 1. 解耦
|
||||
@@ -140,7 +159,24 @@ func ConsumeSeckill() {
|
||||
> [!question]
|
||||
> 如果不需要解耦和削峰,还有必要引入 MQ 吗?
|
||||
|
||||
### MQ 引入的代价
|
||||
|
||||
MQ 不是银弹,引入它就意味着接受以下挑战:
|
||||
|
||||
1. **运维复杂度上升**:多了一个需要监控、扩容、容灾的中间件。Broker 挂了怎么办?消息积压怎么告警?这些都是新增的运维命题。
|
||||
2. **消息丢失与重复**:网络不可靠,消息可能丢、可能重复投递。你需要设计 ACK 机制、幂等消费、甚至 Exactly-Once 语义——这些都不简单。
|
||||
3. **系统可用性转移**:原本你的可用性取决于自己,现在还取决于 Broker。Broker 成了新的单点风险。
|
||||
4. **调试链路变长**:同步调用出了问题,顺着调用栈就能排查。引入 MQ 后,消息可能在队列里"躺"了很久才被消费,排查问题的难度显著增加。
|
||||
|
||||
> [!warning]
|
||||
> 在决定引入 MQ 之前,先问自己:**我真正需要的是解耦、异步还是削峰?** 如果答案都是"不太需要",直接调用可能是更简单可靠的选择。何时该用 MQ 见 [[2-MQ-适用场景与选型原则]]。
|
||||
|
||||
> [!tip]
|
||||
> **一句话总结**:MQ 通过引入"中间人"角色,实现了生产者与消费者之间的解耦、异步和削峰,但代价是更高的系统复杂度和最终一致性。理解这个 trade-off,是用好 MQ 的第一步。
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[2-MQ-适用场景与选型原则]]
|
||||
- [[3-MQ-消息模型]]
|
||||
- [[12-MQ-消息确认与持久化]] — Ack 机制的详细设计
|
||||
- [[28-MQ-Competing-Consumers-模式]] — Partition 并行消费的实战模式
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
---
|
||||
tags:
|
||||
- MQ
|
||||
- 架构
|
||||
- 异步
|
||||
- 选型
|
||||
- 流量削峰
|
||||
create time: 2026-05-24 19:52
|
||||
---
|
||||
|
||||
@@ -125,9 +129,37 @@ MQ 解决了分布式系统中的很多问题,但有些场景引入 MQ 反而
|
||||
|
||||
游戏服务器的实时状态同步、在线聊天等场景要求毫秒级响应。MQ 的存储转发机制会引入额外延迟,直接 WebSocket 或长连接更合适。
|
||||
|
||||
### 你需要 MQ 吗?——决策引导
|
||||
|
||||
在进入选型细节之前,先回答一个更根本的问题:**你的系统是否真的需要 MQ?**
|
||||
|
||||
很多团队在"技术潮流"的驱动下盲目引入 MQ,最后发现自己在用大炮打蚊子。下面这个流程图帮你快速判断:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
START["你有异步通信需求吗?"] -->|否| NO["不需要 MQ,直接调用即可"]
|
||||
START -->|是| Q1{"需要解耦多个下游?"}
|
||||
Q1 -->|是| YES["建议引入 MQ"]
|
||||
Q1 -->|否| Q2{"有流量突增需要削峰?"}
|
||||
Q2 -->|是| YES
|
||||
Q2 -->|否| Q3{"有非核心链路可异步化?"}
|
||||
Q3 -->|是| YES
|
||||
Q3 -->|否| Q4{"需要消息持久化和重试?"}
|
||||
Q4 -->|是| MAYBE["考虑 MQ 或轻量方案"]
|
||||
Q4 -->|否| SIMPLE["考虑 goroutine 池或任务队列"]
|
||||
|
||||
style YES fill:#27AE60,color:#fff
|
||||
style NO fill:#95A5A6,color:#fff
|
||||
style MAYBE fill:#F39C12,color:#fff
|
||||
style SIMPLE fill:#3498DB,color:#fff
|
||||
```
|
||||
|
||||
> [!warning]
|
||||
> 不要因为"别人在用"就引入 MQ。如果你的需求只是简单的后台任务,一个 goroutine 池 + channel 就能搞定,不需要动用分布式 Broker。
|
||||
|
||||
### 选型决策框架
|
||||
|
||||
选型时需要综合考虑以下维度,没有"最好"的 MQ,只有"最合适"的:
|
||||
确认需要 MQ 后,选型时需要综合考虑以下维度。没有"最好"的 MQ,只有"最合适"的:
|
||||
|
||||
| 维度 | Kafka | RabbitMQ | RocketMQ | Pulsar |
|
||||
|------|-------|----------|----------|--------|
|
||||
@@ -138,6 +170,21 @@ MQ 解决了分布式系统中的很多问题,但有些场景引入 MQ 反而
|
||||
| 生态 | 极丰富(Flink/Spark) | 成熟(插件体系) | 较丰富 | 成长中 |
|
||||
| 典型场景 | 大数据管道、日志 | 企业应用、复杂路由 | 电商、金融 | 多租户、云原生 |
|
||||
|
||||
下面是基于核心需求的快速决策路径:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
START["确认需要 MQ"] --> Q1{"核心需求是什么?"}
|
||||
Q1 -->|"大数据 / 日志管道"| Kafka["Kafka"]
|
||||
Q1 -->|"灵活路由 / 企业集成"| RabbitMQ["RabbitMQ"]
|
||||
Q1 -->|"事务消息 / 电商金融"| RocketMQ["RocketMQ"]
|
||||
Q1 -->|"多租户 / 云原生"| Pulsar["Pulsar"]
|
||||
Q1 -->|"极致轻量 / 低延迟"| NATS["NATS"]
|
||||
```
|
||||
|
||||
> [!tip]
|
||||
> 如果你拿不准,**RabbitMQ 是最稳妥的起步选择**——运维简单、文档完善、社区活跃。等业务规模增长后,再根据瓶颈点考虑迁移。更多维度的深度对比见 [[27-MQ-选型对比]]。
|
||||
|
||||
> [!question]
|
||||
> 如果你的团队只有 3 个人,系统日均消息量 10 万条,你会选择哪个 MQ?为什么?
|
||||
|
||||
@@ -156,7 +203,35 @@ MQ 解决了分布式系统中的很多问题,但有些场景引入 MQ 反而
|
||||
|
||||
记住一个原则:**用户需要立即看到结果的操作,不要异步化。**
|
||||
|
||||
**忽略幂等设计**
|
||||
|
||||
这是引入 MQ 后最容易翻车的地方。网络抖动、消费者重启、Broker 重试都可能导致同一条消息被投递多次。如果消费者的处理逻辑不是幂等的,结果就是重复扣款、重复发货、重复积分。
|
||||
|
||||
```go
|
||||
// 非幂等: 消息重投会导致重复扣款
|
||||
func HandlePayment(msg Message) {
|
||||
db.UpdateBalance(msg.UserID, -msg.Amount) // 每次执行都扣一次!
|
||||
}
|
||||
|
||||
// 幂等: 利用消息 ID 做去重
|
||||
func HandlePayment(msg Message) {
|
||||
if db.Exists("processed:" + msg.ID) {
|
||||
return // 已处理过,跳过
|
||||
}
|
||||
db.UpdateBalance(msg.UserID, -msg.Amount)
|
||||
db.Set("processed:" + msg.ID, true)
|
||||
}
|
||||
```
|
||||
|
||||
> [!warning]
|
||||
> 上 MQ 之前,先确保每个消费者都有幂等保障。幂等不是可选项,是必选项。详细设计见 [[14-MQ-消息幂等性]]。
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[1-MQ-基础概念]]
|
||||
- [[3-MQ-消息模型]]
|
||||
- [[1-MQ-基础概念]] — MQ 是什么、核心术语、Pull/Push 模型
|
||||
- [[3-MQ-消息模型]] — 队列与发布订阅模型的深入对比
|
||||
- [[27-MQ-选型对比]] — 七大维度横向对比各主流 MQ
|
||||
- [[14-MQ-消息幂等性]] — 幂等消费的设计方案
|
||||
- [[12-MQ-消息确认与持久化]] — ACK 机制如何保证消息不丢
|
||||
- [[16-MQ-死信队列与消息回溯]] — 消费失败后的兜底处理
|
||||
- [[46-MQ-分布式事务实践]] — 事务消息在电商/金融场景的实战
|
||||
|
||||
Reference in New Issue
Block a user