129 lines
4.8 KiB
Markdown
129 lines
4.8 KiB
Markdown
|
|
---
|
|||
|
|
tags:
|
|||
|
|
- MQ
|
|||
|
|
create time: 2026-05-24 19:52
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# MQ 跨集群复制与容灾
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
当单一 MQ 集群无法满足灾备、合规或多地域低延迟访问的需求时,跨集群复制成为必选项。本文深入分析 Kafka 和 RocketMQ 的跨集群复制方案,探讨 MirrorMaker 2.0 的工作原理,以及异地多活架构的实现思路。
|
|||
|
|
|
|||
|
|
## 正文
|
|||
|
|
|
|||
|
|
### 跨集群复制的需求
|
|||
|
|
|
|||
|
|
为什么不能只靠一个集群?四类典型需求驱动了跨集群复制:
|
|||
|
|
|
|||
|
|
1. **灾备**:主机房不可用时,备集群接管流量,RTO/RPO 需要可控。
|
|||
|
|
2. **数据迁移**:从一个集群迁移到另一个(比如跨云迁移),要求业务无感知。
|
|||
|
|
3. **就近访问**:全球业务需要各区域就近接入,降低网络延迟。
|
|||
|
|
4. **合规要求**:某些数据法规(如 GDPR)要求数据不得离开特定区域,但仍需全局分析。
|
|||
|
|
|
|||
|
|
> [!question]
|
|||
|
|
> 如果你的 MQ 集群需要同时服务中国和欧洲用户,你会选择一个全球集群还是两个区域集群?各自的 Trade-off 是什么?
|
|||
|
|
|
|||
|
|
### Kafka 跨集群方案
|
|||
|
|
|
|||
|
|
**MirrorMaker 2.0**
|
|||
|
|
|
|||
|
|
MirrorMaker 2.0(简称 MM2)是 Kafka 官方的跨集群复制工具,基于 Kafka Connect 构建。它的核心流程很直观:
|
|||
|
|
|
|||
|
|
1. 从远端集群消费消息
|
|||
|
|
2. 写入本地集群的对应 Topic
|
|||
|
|
3. 维护 Offset 映射关系
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
graph LR
|
|||
|
|
subgraph "源集群 DC-A"
|
|||
|
|
TA["Topic-A"]
|
|||
|
|
TB["Topic-B"]
|
|||
|
|
end
|
|||
|
|
subgraph "MirrorMaker 2.0"
|
|||
|
|
C1["Source Connector"]
|
|||
|
|
T["转换层 - Topic重命名, Offset映射"]
|
|||
|
|
C2["Sink Connector"]
|
|||
|
|
end
|
|||
|
|
subgraph "目标集群 DC-B"
|
|||
|
|
TA2["dc-A.Topic-A"]
|
|||
|
|
TB2["dc-A.Topic-B"]
|
|||
|
|
end
|
|||
|
|
TA --> C1
|
|||
|
|
TB --> C1
|
|||
|
|
C1 --> T
|
|||
|
|
T --> C2
|
|||
|
|
C2 --> TA2
|
|||
|
|
C2 --> TB2
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
MM2 的几个关键设计点:
|
|||
|
|
|
|||
|
|
- **Topic 名称转换**:默认在目标集群加前缀(如 `dc-A.topic-A`),避免命名冲突。
|
|||
|
|
- **Offset 映射**:MM2 会创建一个 `__consumer_offsets` 的映射 Topic,记录源集群和目标集群的 Offset 对应关系。这样消费者在故障切换后能从正确的位置继续消费。
|
|||
|
|
- **自动同步配置**:Topic 分区数、副本因子等配置变更也会自动同步。
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// MM2 Offset 映射的核心概念(简化示意)
|
|||
|
|
type OffsetSync struct {
|
|||
|
|
UpstreamOffset int64 // 源集群的 Offset
|
|||
|
|
DownstreamOffset int64 // 目标集群的 Offset
|
|||
|
|
Timestamp int64
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 故障切换时,根据映射找到目标集群的对应 Offset
|
|||
|
|
func TranslateOffset(syncs []OffsetSync, upstreamOffset int64) int64 {
|
|||
|
|
// 二分查找最近的映射点
|
|||
|
|
for i := len(syncs) - 1; i >= 0; i-- {
|
|||
|
|
if syncs[i].UpstreamOffset <= upstreamOffset {
|
|||
|
|
return syncs[i].DownstreamOffset
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Confluent Replicator**
|
|||
|
|
|
|||
|
|
Confluent 的商业方案,相比 MM2 更成熟,支持自动 Topic 创建、配置同步、Offset 翻译,但需要 Confluent Platform 授权。
|
|||
|
|
|
|||
|
|
**Cluster Linking(Confluent 7.0+)**
|
|||
|
|
|
|||
|
|
这是 Confluent 最新的方案,直接在 Broker 层面建立集群间的链接,不需要额外的 Connect 集群。性能更好,延迟更低,但仅限 Confluent 平台。
|
|||
|
|
|
|||
|
|
### RocketMQ 跨集群方案
|
|||
|
|
|
|||
|
|
RocketMQ 的跨集群方案相对简单:
|
|||
|
|
|
|||
|
|
- **DLedger 多副本**:基于 Raft 协议的副本同步,适合同城双活场景,RPO = 0。
|
|||
|
|
- **RocketMQ Bridge**:类似 Kafka MM2 的桥接组件,支持跨集群消息转发。
|
|||
|
|
|
|||
|
|
### 跨地域同步的挑战
|
|||
|
|
|
|||
|
|
跨集群复制不是简单的消息转发,面临几个硬核挑战:
|
|||
|
|
|
|||
|
|
**网络延迟**:跨地域网络延迟通常 50-200ms,同步复制会严重影响吞吐。大多数场景只能用异步复制,这意味着 RPO > 0。
|
|||
|
|
|
|||
|
|
**消息顺序**:源集群保证了分区内的顺序,但复制到目标集群后,由于网络抖动和并行复制,可能出现乱序。解决方案是按 Key 哈希到同一分区,牺牲并行度换取顺序。
|
|||
|
|
|
|||
|
|
**冲突解决**:双向复制(Active-Active)场景下,两端可能同时修改同一份数据。常见策略有 Last-Write-Wins(LWW)和基于版本号的冲突检测。
|
|||
|
|
|
|||
|
|
**带宽成本**:跨地域带宽昂贵。可以通过压缩、批量发送、限速等手段控制成本。
|
|||
|
|
|
|||
|
|
### 异地多活架构
|
|||
|
|
|
|||
|
|
异地多活是最复杂的跨地域方案,核心思想是每个区域都能独立提供完整服务:
|
|||
|
|
|
|||
|
|
- **写本地读本地**:每个区域的 Producer 和 Consumer 都连接本地集群,避免跨地域调用。
|
|||
|
|
- **全局 Topic 路由**:通过全局路由层,将请求导向用户所在的区域集群。
|
|||
|
|
- **冲突检测与合并**:对于需要全局一致的数据,采用 CDC(Change Data Capture)+ 冲突解决机制。
|
|||
|
|
|
|||
|
|
> [!question]
|
|||
|
|
> 异地多活下,如果用户 A 在中国下单,同时用户 B 在美国查看库存,如何保证库存数据的一致性?
|
|||
|
|
|
|||
|
|
## 关联笔记
|
|||
|
|
|
|||
|
|
- [[44-MQ-高可用架构]]
|
|||
|
|
- [[46-MQ-分布式事务实践]]
|
|||
|
|
- [[51-云消息服务]]
|