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 的工作流程大致如下:
- 启动一个 Java 进程,加载配置文件。
- 内部启动一个 Kafka Consumer,从源集群拉取消息。
- Consumer 将消息传递给内部的 Kafka Producer。
- Producer 将消息写入目标集群。
- 整个过程是同步的,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 工作流程:数据是如何流动的?
理解了组件,咱们来看看数据流动的完整链路。我画了一张图,帮你直观理解:
数据流动的具体步骤:
- Source Connector 启动:根据配置文件,确定要复制的 Topic 列表。
- 消费数据:Source Connector 内部的 Kafka Consumer 从源集群拉取消息。
- 记录偏移量:每消费一批消息,就把偏移量写入
connect-offsetsTopic。 - 数据转换:Source Connector 将 ConsumerRecord 转换为 Connect 的 SinkRecord。
- 发送给 Worker:SinkRecord 通过 Connect 框架的内部通道发送给 Worker 节点。
- Sink Connector 接收:Worker 节点将 SinkRecord 分发给对应的 Sink Connector 任务。
- 写入目标集群:Sink Connector 调用 Kafka Producer,将数据写入目标集群的指定 Topic。
- 确认完成: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,尤其是生产环境。虽然配置稍微复杂一点,但换来的是稳定性、可扩展性和可维护性。你想想看,一个能自动容错、自动恢复的复制系统,比一个挂了就要手动重启的系统,省心太多了。