6.0 KiB
tags, create time
| tags | create time | |
|---|---|---|
|
2026-05-24 19:52 |
MQ 与微服务
概述
MQ 是微服务架构中实现服务间异步通信、解耦和削峰的核心组件。本文探讨同步与异步通信的取舍、事件驱动架构、服务网格对 MQ 的影响、背压传播机制以及 Saga 在微服务中的实践。
正文
微服务间通信:同步 vs 异步
微服务之间的通信本质上是两种模式的博弈:
同步通信(HTTP/gRPC):调用方发起请求,阻塞等待响应。优点是简单直观,适合查询类操作和需要即时响应的场景。缺点是强依赖——被调用方不可用时,调用方也会失败。
异步通信(MQ):发送方发出消息后立即返回,不关心消费方何时处理。优点是解耦、削峰、提高系统弹性。缺点是增加了复杂度,调试困难,且不适合需要即时响应的场景。
选择原则很简单:需要即时响应用同步,不需要即时响应用异步。一个典型的电商系统,查询商品详情用 gRPC,下单后发扣库存消息用 MQ。
[!question] 一个用户注册流程需要:创建账号、发欢迎邮件、初始化用户配置。这三步应该用同步还是异步?如果邮件发送失败怎么办?
事件驱动微服务
事件驱动架构(EDA)是异步通信的极致形态。在 EDA 中,每个服务通过领域事件与其他服务交互,没有直接的 API 调用依赖。
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 的流量是否应该走服务网格?
答案通常是不走。原因:
- 协议不匹配:服务网格的 Sidecar 代理(Envoy)擅长处理 HTTP/gRPC,对 Kafka、RocketMQ 等自定义协议支持有限。
- 性能开销:每条消息都经过 Sidecar 代理会增加不必要的延迟。
- 长连接:MQ 客户端通常维护长连接,Sidecar 代理的连接管理可能与之冲突。
不过,mTLS(双向 TLS 认证)是可以应用于 MQ 流量的。可以在客户端 SDK 层面集成 mTLS,而不是依赖 Sidecar。
背压传播
背压(Backpressure)是分布式系统中经常被忽视的问题。当下游处理速度跟不上上游生产速度时,压力会沿链路反向传播:
MQ 堆积 → Consumer 处理不过来 → 数据库连接池耗尽 → 数据库响应变慢 → 更多请求超时 → 系统雪崩
端到端的背压管理需要每一层都参与:
// 带背压控制的消费者
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 还有什么解法?
生产实践中,大多数团队会混合使用两种方式:核心业务流程用编排式(可控制),辅助流程用协同式(松耦合)。