Skip to content

MirrorMaker 2 架构与配置 ​

MirrorMaker 2(简称 MM2)是 Apache Kafka 官方跨集群复制工具,通过 KIP-382 在 Kafka 2.4 引入,替换了 MM1 的 consumer-producer 直连方案。旧版 MM1 已在 Kafka 4.0 被移除,本文只讲 MM2。

1. 为什么不是 MM1 ​

MM1 是一对 consumer + producer 在同一 JVM 里跑:

txt
   [source cluster] ──▶ Consumer ──▶ Producer ──▶ [target cluster]

它主要有 3 点局限,KIP-382 的动机段列得很清楚:

  1. 不同步 offset。MM1 只复制消息,不提供消费组 offset 的检查点与翻译机制,consumer 侧的消费进度停留在源集群的 __consumer_offsets 里。灾备切换后,目标集群的 consumer 不能直接依赖源集群的消费进度,需要另行确定起始 offset。若没有预先设计迁移方案,只能 earliest 从头或 latest 从尾,两者都可能造成重复消费或漏处理。
  2. 不同步 topic 配置和 ACL。分区数、保留时间、副本因子、ACL 都要在目标集群手工重建。
  3. 不支持 Active-Active。缺少循环防护机制。

MM2 基于 Kafka Connect 框架重写,实现了消息复制、offset 检查点、topic 元数据同步和心跳等能力,但这些机制仍需结合目标集群的权限、消费者状态和切换流程配置才能生效。

2. 三个 Connector ​

MM2 本质上是一组 Kafka Connect Connector。启动 connect-mirror-maker.sh 会自动部署三个 Connector(每个 replication flow 一套):

Connector职责输出的内部 Topic
MirrorSourceConnector复制消息数据;同步 topic 配置与 ACL;向 offset-syncs 写入 offset 映射<source>.<topic>、mm2-offset-syncs.<target>.internal(默认位于源集群)
MirrorCheckpointConnector定期读取源集群 consumer group offset,翻译成目标集群 offset 后写入 checkpoint topic<source>.checkpoints.internal
MirrorHeartbeatConnector向源集群写心跳消息,随消息流被 SourceConnector 复制到目标,用于端到端复制延迟监控heartbeats、<source>.heartbeats

三者的关系:

txt
                       source cluster                              target cluster
                     ┌──────────────────┐                       ┌──────────────────┐
      producer ────▶ │  topic: orders   │ ──── MirrorSource ──▶ │ src.orders       │
                     │                  │                       │                  │
                     │  __consumer_off  │ ── MirrorCheckpoint ▶ │ src.checkpoints. │
                     │                  │   translate & write   │  internal        │
                     │  heartbeats      │ ◀── MirrorHeartbeat ─┐│                  │
                     │                  │      write hb        ││ src.heartbeats   │
                     │                  │ ──── MirrorSource ───┴│ (via replication)│
                     └──────────────────┘                       └──────────────────┘

MirrorHeartbeatConnector 是三个 Connector 中专门向源集群写心跳消息的连接器(MirrorSourceConnector 也会向源集群的 offset-syncs topic 写入 offset 映射,但那是内部状态,不是业务数据)。它写的 heartbeats topic 会被 MirrorSourceConnector 当成普通消息复制到目标,形成一条端到端"心跳链路"——目标集群看到的 heartbeat 与心跳时间戳的差值可用于观测端到端复制延迟。心跳反映的是心跳消息自身的传播情况,不能单独证明所有业务 topic 都已追平,生产上还需结合 SourceConnector 的复制延迟指标和分区复制进度一起判断。

数据来源:Kafka MirrorMaker 2 — Multi-Cluster Replication、KIP-382、Red Hat: MirrorMaker components。

3. Remote Topic 命名:循环防护的关键 ​

MM2 复制 topic 时,默认给 topic 加上源集群 alias 作为前缀。这是 DefaultReplicationPolicy 的核心行为。

txt
Source cluster alias: us-east
Source topic:         orders

Target cluster stored: us-east.orders

为什么这么设计?Active-Active 拓扑下,如果不改名字:

  • us-east.orders 消息复制到 us-west 的 orders
  • us-west.orders 又被 MM2 反向复制回 us-east.orders
  • 无限循环,消息量指数级增长

有了前缀,Connector 会解析 topic 名的前缀判断其来源集群,遇到来自目标集群的 remote topic 就跳过,循环由此被切断。

代价:目标集群消费者要订阅 us-east.orders,而不是 orders——应用代码要感知 topic 命名规范。

另一种选择是 IdentityReplicationPolicy,不加前缀。它只适合 Active-Standby 单向复制场景;Active-Active 用它会导致消息在集群间无限循环复制,因此不能用于未经额外设计的双向复制。

properties
# 不加前缀(仅 Active-Standby 可用)
replication.policy.class=org.apache.kafka.connect.mirror.IdentityReplicationPolicy

4. Active-Standby 配置示例 ​

以下配置假设两个 Kafka 集群已可从 MM2 所在环境访问,且运行账户具备创建、读取、写入 topic 及管理 ACL 的权限。示例展示复制方向与常用参数,不含 TLS、SASL 和 Connect Worker 的全部部署设置。

一份典型的 Active-Standby 配置:

properties
# ── 集群 alias ─────────────────────────────
clusters = primary, backup

# ── Bootstrap ──────────────────────────────
primary.bootstrap.servers = kafka-primary-1:9092,kafka-primary-2:9092,kafka-primary-3:9092
backup.bootstrap.servers  = kafka-backup-1:9092,kafka-backup-2:9092,kafka-backup-3:9092

# ── 复制方向:primary → backup ──────────────
primary->backup.enabled  = true
backup->primary.enabled  = false

# ── 复制哪些 topic 和 consumer group ────────
primary->backup.topics         = orders,payments,inventory-.*
primary->backup.topics.exclude = .*\.internal,__.*
primary->backup.groups         = .*
primary->backup.groups.exclude = console-consumer-.*

# ── 副本因子 ───────────────────────────────
replication.factor                       = 3
checkpoints.topic.replication.factor     = 3
heartbeats.topic.replication.factor      = 3
offset-syncs.topic.replication.factor    = 3

# ── 复制策略 ───────────────────────────────
replication.policy.class = org.apache.kafka.connect.mirror.DefaultReplicationPolicy

# ── 同步频率 ───────────────────────────────
emit.heartbeats.interval.seconds    = 1
emit.checkpoints.interval.seconds   = 10
sync.topic.configs.enabled          = true
sync.topic.acls.enabled             = true
refresh.topics.interval.seconds     = 30

# ── 并行度 ─────────────────────────────────
tasks.max = 8

启动:

bash
bin/connect-mirror-maker.sh config/mm2.properties

Active-Active 需要把反向也 enable:

properties
primary->backup.enabled = true
backup->primary.enabled = true

但这只是建立复制链路。上线前还需确认 topic 命名策略、应用订阅规则、双端写入边界和冲突处理方式。若两个集群会同时写入同一业务数据,必须在应用层设计数据归属或冲突解决规则,不能把 MM2 的循环防护机制当作数据一致性机制。

5. 关键配置详解 ​

5.1 sync.topic.configs.enabled ​

默认 true。开启后 SourceConnector 会周期性地把源 topic 的配置(retention.ms、cleanup.policy、max.message.bytes 等)同步到目标 topic。

注意:replication.factor 不会被同步。目标 topic 的副本因子由 MM2 配置里的 replication.factor 决定,默认 2(生产环境应显式设为 3)。

5.2 sync.topic.acls.enabled ​

默认 true。同步 topic 级别的 ACL,但同步范围很窄:仅同步 topic 字面(LITERAL)类型的规则,WRITE 权限会被丢弃,ALL 权限会被降级为 READ。其它资源(group、cluster、transactional-id 等)的 ACL 也不同步,需要另行维护(GitOps 是常见做法)。

5.3 emit.checkpoints.interval.seconds ​

默认 60 秒。MirrorCheckpointConnector 每隔多久把最新的 offset 映射写入 checkpoints topic。灾备切换时消费者能恢复到的 offset 精度受这个间隔影响。示例配置将其设为 10 秒以换取更高的恢复精度,但实际恢复位置还取决于 offset 映射是否及时、复制进度以及切换时的状态。

5.4 sync.group.offsets.enabled(Kafka 2.7+) ​

默认 false。开启后 MirrorCheckpointConnector 会直接把翻译后的 offset 提交到目标集群的 __consumer_offsets,而不仅仅写 checkpoints topic。灾备切换时 consumer 可以直接以原 group.id 启动,无需再用 RemoteClusterUtils 手工翻译。

这一同步只对目标集群中无活跃消费者的 consumer group 生效。若目标集群已有同名 group 的活跃消费者,同步不会执行(官方文档记载此时会返回 UNKNOWN_MEMBER_ID 错误)。

只在灾备场景开启

sync.group.offsets.enabled=true 会让目标集群的 __consumer_offsets 被源集群覆盖。如果目标集群同时有独立的消费者在跑(Active-Active 场景),会互相踩到。只在 Active-Standby 单向复制时开启。

5.5 refresh.topics.interval.seconds ​

默认 600 秒(10 分钟)。周期性检查源集群是否有新 topic 出现。新 topic 会被自动创建对应的 remote topic 并开始复制。示例配置将其设为 30 秒以缩短新 topic 的发现延迟,但更频繁的检查会给 broker 带来额外的 metadata 请求压力,应结合新 topic 的发现延迟要求和 broker 负载权衡。

5.6 内部 topic 的副本因子 ​

checkpoints.topic.replication.factor / heartbeats.topic.replication.factor / offset-syncs.topic.replication.factor 默认都是 3(KIP-382 定义),但很多示例配置里写的是 1——生产环境应显式设为 3,确保内部 topic 具备与业务 topic 相同的容错能力。其中 offset-syncs 保存源端与目标端 offset 的映射关系,映射数据无法写入或读取时,新的 checkpoint 可能无法正确生成,进而影响灾备恢复位置的准确性。

6. 部署形态 ​

MM2 有三种部署方式:

1. Dedicated MM2 Cluster:connect-mirror-maker.sh 启动一个专用 Connect 集群。最常见,运维简单。

2. 复用现有 Kafka Connect 集群:把三个 Connector 手工注册到已有 Connect 集群。适合已经有大量 Connector 的场景。

3. Kubernetes Operator:Strimzi 提供 KafkaMirrorMaker2 CRD,通过声明式方式管理复制方向与 Connector 配置。已使用 Strimzi 管理 Kafka 集群的环境可用它减少手工维护,本质上仍是前两种运行架构之一,只是管理方式不同。

7. 常见坑 ​

坑一:目标 topic 副本因子只有 2。默认值 replication.factor=2,生产环境应显式 replication.factor=3。

坑二:__consumer_offsets 也被复制。默认排除规则 mm2.*\.internal,.*\.replica,__.* 会挡住内部 topic。但排除规则优先于包含规则,用 primary->backup.topics = .* 全量包含时,如果同时用自定义的 primary->backup.topics.exclude 覆盖了默认值且未保留 __.*、.*\.internal,内部 topic 就会被复制。务必确认排除规则里仍包含对 __.* 和 .*\.internal 的排除。

坑三:Active-Active 忘记双向 enable。只 enable 一个方向就是单向复制(Active-Standby),另一方向的消息不会被复制到对端。切换后还需调整复制方向,并确认新主集群的消息与消费者状态。

坑四:延迟只看 record-count。衡量 MM2 复制链路延迟的指标是 replication-latency-ms(KIP-382 定义),基于消息 timestamp 与到达 target 的时间差。它反映复制链路自身的延迟估计,不等于业务端到端处理延迟,也不能替代消费者 lag。

坑五:MirrorHeartbeatConnector 部署位置。它向源集群写心跳,如果 MM2 部署在离目标近的位置(推荐做法,减少复制延迟),HeartbeatConnector 会跨机房写。Red Hat 官方文档建议灾备场景关掉 HeartbeatConnector,仅在需要监控端到端延迟时开启。

8. 与本目录其他章节的关系 ​

Released under the MIT License.