--- tags: - MQ create time: 2026-05-24 19:52 --- # MQ 高可用架构 ## 概述 消息队列作为分布式系统的核心基础设施,其高可用性直接影响整个业务链路的稳定性。本文从可用性指标、Broker 集群模式、无单点设计、跨机房容灾、灰度发布等维度,系统梳理 MQ 高可用架构的设计要点。 ## 正文 ### 高可用指标 衡量高可用有两个核心维度:**可用性指标**和**恢复指标**。 可用性用"几个 9"来衡量: | 级别 | 可用性 | 年停机时间 | 典型场景 | |------|--------|-----------|---------| | 3 个 9 | 99.9% | 8.76 小时 | 内部工具 | | 4 个 9 | 99.99% | 52.6 分钟 | 核心业务 | | 5 个 9 | 99.999% | 5.26 分钟 | 金融交易 | 恢复指标有两个: - **RTO(Recovery Time Objective)**:从故障到恢复的时间目标,比如 RTO = 5 分钟意味着故障后 5 分钟内必须恢复。 - **RPO(Recovery Point Objective)**:可容忍的最大数据丢失量。RPO = 0 表示零数据丢失,这通常需要同步复制。 > [!question] > 你的业务能接受多长的 MQ 不可用时间?如果 RPO = 0,同步复制带来的性能下降你能接受吗? ### Broker 集群模式 MQ 的高可用核心在于 Broker 层的集群化。不同产品有不同的实现路径,但本质都是**数据冗余 + 故障切换**。 **主从复制(Leader-Follower)** 最经典的模式。Leader 处理读写请求,Follower 同步数据作为备份。 - **同步复制**:Producer 发送消息后,Leader 等待 Follower 确认才返回 ACK。数据不丢,但延迟增加。 - **异步复制**:Leader 写入本地即返回 ACK,Follower 异步拉取。性能好,但主宕机可能丢数据。 **多副本 ISR(In-Sync Replicas)** Kafka 的核心机制。ISR 是一组与 Leader 保持同步的副本集合。如果某个 Follower 落后太多,会被踢出 ISR。只有 ISR 中的副本才有资格被选为新 Leader。 ```go // ISR 维护的核心逻辑(简化示意) type ISRManager struct { leader int32 isr map[int32]bool lagThresh int64 // 允许的最大滞后字节数 } // Follower 拉取数据后更新 LEO,Leader 检查是否还在 ISR 中 func (m *ISRManager) UpdateISR(replicaID int32, logEndOffset int64, leaderLEO int64) { if leaderLEO-logEndOffset > m.lagThresh { delete(m.isr, replicaID) // 落后太多,踢出 ISR } } ``` **Raft 共识** Raft 是强一致性协议,天然适合做 MQ 的副本同步。Kafka 从 3.3 开始用 KRaft(基于 Raft 的 Controller)替代 ZooKeeper;RocketMQ 5.0 引入 DLedger(也是 Raft)实现多副本。 ```go // Raft 核心:多数派确认后才提交 type RaftLog struct { term uint64 index uint64 data []byte } // 只有超过半数节点写入成功,日志才算 committed func (r *RaftNode) AppendEntries(entries []RaftLog) bool { ackCount := 1 // Leader 自己 for _, peer := range r.peers { if r.sendAppendRPC(peer, entries) { ackCount++ } } return ackCount > len(r.peers)/2 } ``` ### 无单点设计 一个真正的高可用集群不允许任何单点故障。 | 产品 | 传统单点 | 无单点方案 | |------|---------|-----------| | RocketMQ | NameServer | NameServer 本身就是无状态集群,任意节点宕机不影响服务 | | Kafka | ZooKeeper | KRaft 模式下 Controller 使用 Raft 选出 Leader,不再依赖 ZK | | RabbitMQ | 队列所在节点 | Quorum Queue 基于 Raft 多副本,队列数据分布在多个节点 | > [!question] > NameServer 是无状态的,那它和 ZooKeeper 的设计思路有什么根本区别? ### 跨机房容灾 单机房再怎么高可用,也挡不住机房级故障(断电、光纤被挖断、自然灾害)。跨机房容灾有几种常见模式: - **同城双活**:两个机房距离 50km 以内,网络延迟 < 2ms。MQ 集群可以同步复制,RPO = 0。 - **异地多活**:多地部署独立集群,各自处理就近流量,异步复制数据。RPO > 0,但抗灾能力最强。 - **灾备切换**:主机房写入,备机房冷备或温备。故障时手动或自动切换,RTO 较长。 ### 灰度发布 MQ 作为基础设施,升级必须谨慎。一次糟糕的滚动升级可能让整个集群瘫痪。 - **滚动升级**:逐台替换 Broker,每次只升级一台,确认无异常后再继续。Kafka 和 RocketMQ 官方文档都推荐这种方式。 - **蓝绿部署**:部署一套全新版本的集群,流量验证通过后一次性切换。 - **金丝雀发布**:先将少量流量导入新版本集群,观察指标稳定后再全量切换。 ```mermaid graph TB subgraph "机房A - 主集群" B1["Broker1 Leader"] B2["Broker2 Follower"] B3["Broker3 Follower"] end subgraph "机房B - 备集群" B4["Broker4 Leader"] B5["Broker5 Follower"] B6["Broker6 Follower"] end subgraph "机房C - 观察节点" B7["Broker7 Observer"] end P["Producer Group"] C["Consumer Group"] P --> B1 P --> B4 B1 -->|"同步复制"| B2 B1 -->|"同步复制"| B3 B4 -->|"异步复制"| B5 B4 -->|"异步复制"| B6 B1 -->|"跨机房复制"| B4 B4 -->|"观察同步"| B7 B1 --> C B4 --> C ``` ### 一致性与性能的权衡 高可用不是免费的。更多的副本、更强的一致性意味着更高的延迟和更低的吞吐。生产实践中需要根据业务特点选择合适的方案: - 订单、支付类:同步复制,宁可慢也不能丢。 - 日志、埋点类:异步复制,允许少量丢失,追求高吞吐。 - 通知、推送类:异步复制 + 重试,最终一致即可。 ## 关联笔记 - [[45-MQ-跨集群复制与容灾]] - [[46-MQ-分布式事务实践]] - [[49-MQ-客户端连接管理]] - [[50-MQ-设计与实现]]