Files
cs-note/hhs/MQ/12-架构与实战/44-MQ-高可用架构.md
T
2026-05-24 20:51:06 +08:00

5.8 KiB
Raw Blame History

tags, create time
tags create time
MQ
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。

// 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)实现多副本。

// 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 官方文档都推荐这种方式。
  • 蓝绿部署:部署一套全新版本的集群,流量验证通过后一次性切换。
  • 金丝雀发布:先将少量流量导入新版本集群,观察指标稳定后再全量切换。
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

一致性与性能的权衡

高可用不是免费的。更多的副本、更强的一致性意味着更高的延迟和更低的吞吐。生产实践中需要根据业务特点选择合适的方案:

  • 订单、支付类:同步复制,宁可慢也不能丢。
  • 日志、埋点类:异步复制,允许少量丢失,追求高吞吐。
  • 通知、推送类:异步复制 + 重试,最终一致即可。

关联笔记