2. Kafka Connect 架构
Kafka Connect 的架构,说白了就是一套「插件化」的数据搬运流水线。我刚开始接触时也觉得组件有点多,但拆开来看,每个角色各司其职,并不复杂。
先看一张整体架构图,把几个核心组件的关系理清楚:
Worker:集群里的「打工人」
Worker 是一个 Java 进程,负责执行 Connector 和 Task。它有两种运行模式:
- 单机模式(Standalone):一个进程搞定所有事。适合开发测试,或者数据量很小的场景。
- 分布式模式(Distributed):多个 Worker 组成集群,自动负载均衡、故障转移。生产环境几乎都用这个。
我的经验:单机模式我一般只在本地调试用。线上环境千万别图省事,一旦 Worker 挂了,整个数据管道就断了。分布式模式下,即使一个 Worker 宕机,其他 Worker 会自动接管它的 Task。
Worker 启动时会加载所有插件(Connector、Converter、Transforms 的 jar 包),然后通过 REST API 接收用户提交的 Connector 配置。我个人习惯把插件目录独立出来,方便版本管理。
Connector:管道的「规划师」
Connector 负责定义「怎么连接外部系统」。它不直接搬运数据,而是制定搬运计划。
两种类型:
| 类型 | 方向 | 典型场景 |
|---|---|---|
| SourceConnector | 外部系统 → Kafka | 从 MySQL、PostgreSQL、文件系统采集数据 |
| SinkConnector | Kafka → 外部系统 | 写入 Elasticsearch、HDFS、S3 |
Connector 的核心工作是「划分 Task」。比如一个 MySQL SourceConnector 要同步 10 张表,它可以决定拆成 5 个 Task,每个 Task 负责 2 张表。这个拆分逻辑由 Connector 的 taskConfigs() 方法决定。
关键点:Connector 本身不处理数据,它只负责「规划」。真正干活的是 Task。
Task:真正搬砖的「苦力」
Task 是 Kafka Connect 里唯一干实事的组件。它是一个线程,负责:
- Source Task:从数据源拉取记录,交给 Kafka Producer 发送到 Topic。
- Sink Task:从 Kafka Consumer 拉取记录,写入目标系统。
每个 Task 都有自己独立的状态和偏移量(offset)。这意味着 Task 之间互不干扰,可以并行执行。
举个例子:
// SourceConnector 决定拆成 3 个 Task
@Override
public List<Map<String, String>> taskConfigs(int maxTasks) {
List<Map<String, String>> configs = new ArrayList<>();
// 把 6 张表分成 3 组,每组 2 张表
configs.add(Map.of("tables", "users,orders"));
configs.add(Map.of("tables", "products,inventory"));
configs.add(Map.of("tables", "payments,shipments"));
return configs;
}
每个 Task 独立运行,互不阻塞。我曾经遇到过一个场景:某个 Task 因为目标系统响应慢导致积压,但其他 Task 完全不受影响。这就是 Task 隔离性的好处。
注意:Task 数量不是越多越好。每个 Task 都会占用一个线程和连接资源。我建议 Task 数不超过 Worker 核数的 2-3 倍,否则线程切换开销会抵消并行收益。
Converter:数据的「翻译官」
Kafka 内部存储的是字节数组。Converter 负责把数据从「外部格式」翻译成「Kafka 内部格式」,或者反过来。
常用的 Converter:
| Converter | 特点 | 适用场景 |
|---|---|---|
| JsonConverter | 可读性好,带 Schema 或不带 Schema | 调试、与 JavaScript 系统对接 |
| AvroConverter | 压缩率高,Schema 演化能力强 | 生产环境,需要 Schema Registry |
| ProtobufConverter | 性能好,gRPC 生态友好 | 微服务架构 |
| StringConverter | 最简单,纯文本 | 日志、简单消息 |
我个人在生产环境首选 AvroConverter。原因很简单:Schema 演化。你想想看,如果上游改了字段类型,下游没感知,数据就坏了。Avro 配合 Schema Registry 可以优雅处理这种变化。
避坑指南:我曾经遇到一个线上事故——JsonConverter 默认不带 Schema,结果上游新增了一个字段,下游解析直接报错。从那以后,凡是涉及多团队协作的数据管道,我都强制使用 AvroConverter + Schema Registry。
Transforms:数据的「美容师」
Transforms 是轻量级的数据处理插件,在数据进入 Kafka(Source)或写入目标系统(Sink)之前执行。它不需要启动流处理引擎,直接在 Connect 内部完成。
内置的 Transforms 已经覆盖了大部分常见需求:
| Transform | 作用 | 示例 |
|---|---|---|
| RenameField | 重命名字段 | old_name → new_name |
| MaskField | 脱敏字段 | 手机号:138****1234 |
| TimestampRouter | 按时间路由 Topic | topic-2024-01 |
| InsertField | 插入静态字段 | 添加 source=mysql |
| Filter | 过滤记录 | 只保留 status=active |
配置方式很简单,在 Connector 配置里声明即可:
transforms=rename,mask
transforms.rename.type=org.apache.kafka.connect.transforms.ReplaceField$Value
transforms.rename.renames=user_name:username,phone:mobile
transforms.mask.type=org.apache.kafka.connect.transforms.MaskField$Value
transforms.mask.fields=phone
transforms.mask.replacement=****
嗯,这里要注意:Transforms 的执行顺序就是配置顺序。上面先重命名,再脱敏,顺序错了可能出问题。
我的建议:Transforms 适合做「轻量级」处理。如果要做聚合、关联、窗口计算,还是交给 Kafka Streams 或 ksqlDB。别把 Connect 当流处理引擎用,它扛不住。
组件协作流程
最后,我把整个协作流程串起来,方便你理解:
- 用户通过 REST API 提交 Connector 配置。
- Worker 集群的 Leader 收到请求,把配置写入 Kafka 内部 Topic(
connect-configs)。 - 所有 Worker 监听配置变更,Leader 根据配置创建 Connector 实例。
- Connector 调用
taskConfigs()生成 Task 配置,Leader 把 Task 分配给各个 Worker。 - 每个 Worker 启动 Task 线程,Task 加载 Converter 和 Transforms 配置。
- Source Task 拉取数据 → Converter 序列化 → Transforms 处理 → 写入 Kafka Topic。
- Sink Task 从 Kafka Topic 消费 → Converter 反序列化 → Transforms 处理 → 写入目标系统。
- Worker 定期把偏移量提交到 Kafka 内部 Topic(
connect-offsets),保证断点续传。
这个流程里,每一步都有对应的配置和调优空间。后面几章我会逐个展开讲,这里先有个整体印象就行。