2、架构原理:MirrorMaker 1.x vs 2.x 架构对比、内部组件与工作流程

跨集群数据复制,说白了就是把一个 Kafka 集群的数据搬到另一个集群。听起来简单,但真做起来坑不少。MirrorMaker 是 Kafka 官方提供的工具,从 1.x 到 2.x,架构变化非常大。我刚开始用 1.x 的时候,总觉得它像个“黑盒子”——能跑,但出了问题很难排查。后来 2.x 出来了,我才真正理解它内部是怎么工作的。

这一节,咱们就掰开揉碎,看看两个版本的架构差异、核心组件,以及数据到底是怎么流过去的。

2.1 MirrorMaker 1.x:经典但粗糙

1.x 的架构非常简单。它本质上就是一个消费者 + 生产者的组合。你想想看,一个进程从源集群消费数据,然后直接生产到目标集群。嗯,就是这么直白。

核心问题:1.x 是一个单进程模型。如果这个进程挂了,复制就停了。而且它不支持多线程并行消费同一个 Topic 的分区,容易造成消费积压。

我记得有一次线上事故,源集群流量突然暴增,MirrorMaker 1.x 进程直接 OOM 了。重启之后发现,消费偏移量已经落后了 2 个小时。那叫一个惨。

1.x 的工作流程大致如下:

  1. 启动一个 Java 进程,加载配置文件。
  2. 内部启动一个 Kafka Consumer,从源集群拉取消息。
  3. Consumer 将消息传递给内部的 Kafka Producer。
  4. Producer 将消息写入目标集群。
  5. 整个过程是同步的,Consumer 的 poll 和 Producer 的 send 在同一个线程里串行执行。

说白了,1.x 就是一个“搬运工”,但只有一个工人,效率低,还容易累倒。

2.2 MirrorMaker 2.x:基于 Connect 的分布式架构

2.x 完全重写了。它不再是一个独立的进程,而是基于 Kafka Connect 框架构建的。这意味着什么呢?意味着它天生就是分布式的、可扩展的、容错的。

我个人习惯把 2.x 看作一个“复制集群”,而不是一个“复制进程”。你可以启动多个 Worker 节点,它们协同工作,即使某个节点挂了,其他节点会自动接管任务。

我的建议:如果你在生产环境使用 MirrorMaker,直接上 2.x。1.x 只适合做简单的测试或临时数据迁移。

2.x 的核心组件有两个:Source Connector 和 Sink Connector。它们分别负责从源集群读取数据和向目标集群写入数据。

2.2.1 Source Connector(源连接器)

Source Connector 运行在源集群侧。它的任务是从源集群消费数据,然后把数据转换成 Connect 内部的 SinkRecord 格式,再通过 Connect 框架发送给 Worker 节点。

这里有个关键点:Source Connector 会跟踪消费偏移量,并且把偏移量信息存储在 Kafka 的内部 Topic(connect-offsets)中。这样即使 Worker 重启,也能从断点处继续复制。

2.2.2 Sink Connector(目标连接器)

Sink Connector 运行在目标集群侧。它从 Connect 框架接收 SinkRecord,然后调用 Kafka Producer 将数据写入目标集群。

2.x 的 Sink Connector 支持自动创建 Topic,并且可以配置 Topic 的副本数、分区数等参数。这一点比 1.x 方便太多了。

我曾经踩过的坑:2.x 默认会复制消费组的偏移量。如果你在目标集群也启动了消费者,它们可能会从错误的偏移量开始消费。记得在配置里把 sync.group.offsets.enabled 设为 false,除非你明确知道自己在做什么。

2.3 架构对比:1.x vs 2.x

为了让你看得更清楚,我整理了一个对比表:

对比维度 MirrorMaker 1.x MirrorMaker 2.x
底层框架 独立 Java 进程 基于 Kafka Connect
部署模式 单进程 分布式 Worker 集群
容错性 差,进程挂了就停 好,Worker 故障自动转移
扩展性 差,单线程消费 好,支持多线程、多 Worker
偏移量管理 依赖 Consumer Group 存储在 Connect 内部 Topic
Topic 自动创建 不支持 支持,可配置
配置复杂度 低 中等,需要理解 Connect 概念
适用场景 简单复制、临时迁移 生产环境、大规模复制

2.4 工作流程:数据是如何流动的?

理解了组件,咱们来看看数据流动的完整链路。我画了一张图,帮你直观理解:

MirrorMaker 2.x 数据流架构图 源 Kafka 集群 Topic-A (分区0,1,2) Source Connector 消费 + 转换 偏移量 → connect-offsets Kafka Connect Worker 集群 Sink Connector 接收 + 写入 自动创建 Topic 目标 Kafka 集群 Topic-A (分区0,1,2) 说明: Source Connector 从源集群消费数据,通过 Connect 框架传递给 Sink Connector,最终写入目标集群。

数据流动的具体步骤:

  1. Source Connector 启动:根据配置文件,确定要复制的 Topic 列表。
  2. 消费数据:Source Connector 内部的 Kafka Consumer 从源集群拉取消息。
  3. 记录偏移量:每消费一批消息,就把偏移量写入 connect-offsets Topic。
  4. 数据转换:Source Connector 将 ConsumerRecord 转换为 Connect 的 SinkRecord。
  5. 发送给 Worker:SinkRecord 通过 Connect 框架的内部通道发送给 Worker 节点。
  6. Sink Connector 接收:Worker 节点将 SinkRecord 分发给对应的 Sink Connector 任务。
  7. 写入目标集群:Sink Connector 调用 Kafka Producer,将数据写入目标集群的指定 Topic。
  8. 确认完成:Sink Connector 向 Connect 框架报告写入成功,框架再通知 Source Connector 提交偏移量。

关键点:2.x 的“至少一次”语义保证。如果 Sink Connector 写入失败,Source Connector 不会提交偏移量,下次会重新消费这批数据。这可能导致重复数据,但不会丢数据。

2.5 配置示例:快速上手 2.x

光说不练假把式。我给你一个最简单的配置示例,让你感受一下 2.x 的配置风格:

# mm2.properties
clusters = source, target

# 源集群配置
source.bootstrap.servers = source-kafka:9092
source.security.protocol = SASL_SSL

# 目标集群配置
target.bootstrap.servers = target-kafka:9092
target.security.protocol = SASL_SSL

# 复制方向:从 source 到 target
source->target.enabled = true

# 复制的 Topic 模式
source->target.topics = .*

# 复制消费组偏移量(建议关闭)
source->target.sync.group.offsets.enabled = false

# 自动创建 Topic
source->target.topic.auto.create = true
source->target.topic.configs = replication.factor=3,min.insync.replicas=2

启动命令也很简单:

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

嗯,就这么几行配置,一个跨集群复制任务就跑起来了。相比 1.x 那种需要手动写 Consumer 和 Producer 代码的方式,2.x 简直是降维打击。

2.6 小结

MirrorMaker 从 1.x 到 2.x,不仅仅是版本号的升级,更是架构思想的转变。1.x 是“一个进程干到底”,2.x 是“分布式协作,各司其职”。

我个人更推荐 2.x,尤其是生产环境。虽然配置稍微复杂一点,但换来的是稳定性、可扩展性和可维护性。你想想看,一个能自动容错、自动恢复的复制系统,比一个挂了就要手动重启的系统,省心太多了。