Flink Source Connector 是 Flink 数据处理架构中关键的一部分,用于从外部系统读取数据流入 Flink 应用程序。它的设计架构高度抽象化,既支持标准化的开发流程,又具备扩展性以满足多种数据源需求。
架构组成
Flink Source Connector 的核心由以下模块组成:
-
Source Interface (源接口)
- Flink 引入
Source接口 (Flink 1.11 开始),该接口抽象化了从数据源读取数据的过程,分为 批处理 和 流式处理 两种模式。 - 主要方法包括:
createReader():创建数据读取器。splitEnumerator():为数据源分片(splits)。getBoundedness():定义数据源是有限数据(bounded)还是无限数据(unbounded)。
- Flink 引入
-
SplitEnumerator (分片枚举器)
- 管理数据分片的生成和分配,负责将数据分片(splits)分配给并行任务。
- 主要职责:
- 动态分配分片到下游任务。
- 监控数据源的可用性,支持动态扩展分片。
-
SplitReader (分片读取器)
- 在每个 Task 中运行,用于从特定分片中提取数据。
- 处理复杂的数据提取逻辑,如 API 调用、文件读取等。
-
SourceReader (数据读取器)
- 调用
SplitReader来实际读取数据,并管理数据流的转换。 - 提供了
pollNext()方法供 Flink 调用以拉取数据。
- 调用
-
Checkpoint 支持
- 实现
CheckpointedFunction接口以支持一致性。 - 通过保存分片的状态(如文件偏移量、Kafka 消费者的位点)实现容错。
在 Flink Source Connector 架构中,
SourceReader和SplitReader是两个独立但紧密相关的组件,分别承担着不同的职责。以下是它们的区别和详细解析:
- 实现
-
SourceReader和SplitReader区别
| 特性 | SourceReader | SplitReader |
|---|---|---|
| 作用层次 | 高层抽象,负责分片管理和数据协调 | 底层实现,负责实际数据读取 |
| 处理对象 | 多个分片(Splits) | 单个分片 |
| 与 Flink 的交互 | 与 Flink 任务直接交互,管理数据流动 | 与外部数据源交互,完成数据提取 |
| 状态管理 | 负责存储和恢复分片的状态 | 通常无状态,专注于数据读取 |
设计模式
-
分片架构
- 分片 (Split) 是 Flink Source 的核心概念,通常与具体的物理数据单元(如 Kafka 分区、HDFS 文件块)一一对应。
- Flink 使用
SplitEnumerator来生成和管理分片,分片信息通过 Checkpoint 保存以支持故障恢复。
-
并行化模型
- Source 是一个高度并行化的组件,
SplitEnumerator将分片分配到不同的并行任务(Task)中,每个任务独立读取自己的分片数据。
- Source 是一个高度并行化的组件,
-
Pull 模式与 Push 模式
- Pull 模式:SourceReader 主动从数据源中提取数据(如 Kafka 消费)。
- Push 模式:数据源通过回调或其他方式将数据推送到 Flink。
高性能与扩展性设计
-
批流统一
- 通过统一的
Source接口支持批处理和流处理,用户无需为不同模式编写额外代码。 - 采用
Boundedness标志来区分有限和无限数据源。
- 通过统一的
-
背压处理
- Flink Source 内置背压处理机制,
SourceReader根据 Flink 的消费速度动态调整数据读取速率。
- Flink Source 内置背压处理机制,
-
动态分片
- 动态扩展分片能力,支持新增数据源或重新分配已有分片,提高任务的灵活性。
典型实现
-
Kafka Source
- 采用分片模型将 Kafka 的分区映射为
KafkaPartitionSplit。 SplitReader直接调用 Kafka 消费者 API 读取分片数据。- 支持 Kafka 的 offset 提交与恢复,确保消费一致性。
- 采用分片模型将 Kafka 的分区映射为
-
File Source
- 将文件分块(block)作为分片。
- 支持多种文件格式(如 CSV、Parquet)和文件系统(如 HDFS、S3)。
-
自定义 Source
- 用户可以实现
Source接口,并通过定义SplitEnumerator和SourceReader来扩展数据源支持。
- 用户可以实现
Source Connector 的运行机制
Source Connector 的运行机制由以下阶段组成:
阶段 1:初始化
-
作业提交
- 用户通过
ExecutionEnvironment提交 Flink 作业,其中定义了使用的Source。 - JobManager 将
Source转化为逻辑执行计划,分配给相应的 TaskManager。
- 用户通过
-
初始化 SplitEnumerator
SplitEnumerator在 JobManager 中启动:- 检查外部数据源的元数据(如 Kafka 分区、文件列表)。
- 将数据源逻辑分片(splits),例如按 Kafka 分区或文件块划分。
- 通过
SplitEnumeratorContext,JobManager 将 Split 分片信息存储,并等待分片调度。
阶段 2:分片分配
-
Split 分配策略
- JobManager 调用
SplitEnumerator的assignSplits()方法,将分片分配到下游的 Source 算子。 - 分配策略:
- 静态分配:在作业启动时一次性分配所有分片。
- 动态分配:分片动态生成并实时分配,常用于 Kafka 或日志流式数据。
- JobManager 调用
-
TaskManager 接收分片
- TaskManager 中的 Source 算子通过网络接收来自 JobManager 的分片信息。
SourceReader调用addSplits()方法加载分片,并为每个分片创建相应的SplitReader。
阶段 3:数据提取
-
SourceReader 驱动 SplitReader
SourceReader在 TaskManager 上运行,通过调用SplitReader的fetch()方法读取数据。SplitReader与外部数据源交互,提取特定分片的数据。例如:- 从 Kafka 分区中消费消息。
- 从文件中读取行或块。
-
数据封装与输出
SourceReader将从多个分片中提取的数据封装为 Flink 的内部数据格式RecordsWithSplitIds。- 数据通过 Flink 的内部网络传输机制,传递到下游算子。
阶段 4:状态管理
-
Checkpoint 机制
- Source 算子实现了 Flink 的
CheckpointedFunction接口,每次 Checkpoint 时:SourceReader将当前处理的分片状态(如 Kafka offset、文件偏移量)保存到 Flink 的状态后端。- JobManager 收集所有 TaskManager 的分片状态,统一存储。
- Source 算子实现了 Flink 的
-
故障恢复
- 在 TaskManager 故障时,JobManager 会重新调度任务,并将最近一次 Checkpoint 的分片状态重新分配到新的 TaskManager。
- TaskManager 恢复后,
SourceReader从 Checkpoint 的状态开始重新消费数据。
总结
Flink Source Connector 的架构通过抽象化接口和模块化设计实现了高性能与易扩展性,能够支持各种数据源的接入。其核心理念是 分片化、并行化、容错性,在批流统一框架下实现一致性消费和动态负载调整。