Files
cs-note/hhs/MQ/12-架构与实战/47-MQ-与微服务.md
T
2026-05-24 20:51:06 +08:00

134 lines
6.0 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 的影响、背压传播机制以及 Saga 在微服务中的实践。
## 正文
### 微服务间通信:同步 vs 异步
微服务之间的通信本质上是两种模式的博弈:
**同步通信(HTTP/gRPC)**:调用方发起请求,阻塞等待响应。优点是简单直观,适合查询类操作和需要即时响应的场景。缺点是强依赖——被调用方不可用时,调用方也会失败。
**异步通信(MQ)**:发送方发出消息后立即返回,不关心消费方何时处理。优点是解耦、削峰、提高系统弹性。缺点是增加了复杂度,调试困难,且不适合需要即时响应的场景。
选择原则很简单:**需要即时响应用同步,不需要即时响应用异步**。一个典型的电商系统,查询商品详情用 gRPC,下单后发扣库存消息用 MQ。
> [!question]
> 一个用户注册流程需要:创建账号、发欢迎邮件、初始化用户配置。这三步应该用同步还是异步?如果邮件发送失败怎么办?
### 事件驱动微服务
事件驱动架构(EDA)是异步通信的极致形态。在 EDA 中,每个服务通过**领域事件**与其他服务交互,没有直接的 API 调用依赖。
```mermaid
graph TD
OS["订单服务"] -->|"OrderCreated"| MQ["MQ Broker"]
MQ -->|"OrderCreated"| IS["库存服务"]
MQ -->|"OrderCreated"| PS["支付服务"]
MQ -->|"OrderCreated"| NS["通知服务"]
IS -->|"InventoryReserved"| MQ
MQ -->|"InventoryReserved"| PS
PS -->|"PaymentCompleted"| MQ
MQ -->|"PaymentCompleted"| OS
MQ -->|"PaymentCompleted"| NS
NS -->|"OrderCompleted"| MQ
```
EDA 的关键好处:
- **松耦合**:订单服务不需要知道有哪些服务消费了 `OrderCreated` 事件,新增消费者不需要改订单服务。
- **可扩展**:要加一个新的"积分服务",只需订阅 `OrderCreated` 事件,无需改动任何现有服务。
- **弹性**:某个消费者宕机,消息在 MQ 中堆积,恢复后继续处理,不影响生产者。
但 EDA 也有代价:**调试困难**(请求链路不直观)、**数据一致性复杂**(最终一致而非强一致)、**事件风暴**(一个事件触发一连串事件)。
### 服务发现与 MQ
微服务需要知道 MQ Broker 的地址。通常有几种方式:
- **配置中心**:将 Broker 地址放在 Nacos/Apollo 等配置中心,客户端动态获取。
- **环境变量**:简单直接,适合容器化部署。
- **DNS SRV 记录**:通过 DNS 解析 Broker 地址,天然支持负载均衡。
多集群场景下,还需要**路由策略**——根据 Topic、地域或负载情况选择目标集群。
### 服务网格与 MQ
服务网格(Istio/Linkerd)通过 Sidecar 代理拦截服务间的网络流量,实现流量管理、安全和可观测性。但 MQ 的流量是否应该走服务网格?
答案通常是**不走**。原因:
1. **协议不匹配**:服务网格的 Sidecar 代理(Envoy)擅长处理 HTTP/gRPC,对 Kafka、RocketMQ 等自定义协议支持有限。
2. **性能开销**:每条消息都经过 Sidecar 代理会增加不必要的延迟。
3. **长连接**:MQ 客户端通常维护长连接,Sidecar 代理的连接管理可能与之冲突。
不过,**mTLS**(双向 TLS 认证)是可以应用于 MQ 流量的。可以在客户端 SDK 层面集成 mTLS,而不是依赖 Sidecar。
### 背压传播
背压(Backpressure)是分布式系统中经常被忽视的问题。当下游处理速度跟不上上游生产速度时,压力会沿链路反向传播:
**MQ 堆积** → **Consumer 处理不过来** → **数据库连接池耗尽** → **数据库响应变慢** → **更多请求超时** → **系统雪崩**
端到端的背压管理需要每一层都参与:
```go
// 带背压控制的消费者
func ConsumeWithBackpressure(consumer MQConsumer, db *sql.DB) {
semaphore := make(chan struct{}, 100) // 最多 100 个并发处理
for msg := range consumer.Messages() {
semaphore <- struct{}{} // 获取信号量,满了就阻塞
go func(m Message) {
defer func() { <-semaphore }() // 释放信号量
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := processMessage(ctx, db, m); err != nil {
// 处理失败,不 ACK,消息会重新投递
consumer.Nack(m)
} else {
consumer.Ack(m)
}
}(msg)
}
}
```
核心手段包括:**限流**(控制消费速率)、**降级**(非核心处理逻辑延后)、**熔断**(下游不可用时快速失败)。
### Saga 在微服务中的实践
前面分布式事务篇提到的 Saga 模式,在微服务中有两种实现方式:
**编排式 Saga(Orchestration)**:由一个 Saga 编排器(Orchestrator)集中控制流程。编排器通过 MQ 向各服务发送命令,等待执行结果,再决定下一步。
- 优点:流程清晰,易于监控和调试。
- 缺点:编排器成为单点和瓶颈。
**协同式 Saga(Choreography)**:各服务通过事件自主协作,没有中心控制点。每个服务监听自己关心的事件,处理后发布新事件。
- 优点:完全去中心化,服务间松耦合。
- 缺点:流程分散在各服务中,难以理解和调试。
> [!question]
> 微服务架构中,如果一个服务下线了,它订阅的事件会堆积。除了扩 Partition 还有什么解法?
生产实践中,大多数团队会混合使用两种方式:核心业务流程用编排式(可控制),辅助流程用协同式(松耦合)。
## 关联笔记
- [[44-MQ-高可用架构]]
- [[46-MQ-分布式事务实践]]
- [[48-MQ-客户端-SDK-最佳实践]]
- [[49-MQ-客户端连接管理]]