Flink CDC系列之: Kafka 数据接收器工厂类KafkaDataSinkFactory
这是一个 Kafka 数据接收器工厂类,负责创建和配置 Kafka Sink 实例。
类概述
java
/** A dummy {@link DataSinkFactory} to create {@link KafkaDataSink}. */
public class KafkaDataSinkFactory implements DataSinkFactory {
public static final String IDENTIFIER = "kafka";
// 实现 DataSinkFactory 接口的方法
}
这个工厂类实现了 Flink CDC 的 DataSinkFactory 接口,专门用于创建 KafkaDataSink 实例。
核心方法解析
createDataSink() - 创建 Sink 实例
java
@Override
public DataSink createDataSink(Context context) {
// 1. 获取基础配置
KeyFormat keyFormat = context.getFactoryConfiguration().get(KEY_FORMAT);
JsonSerializationType jsonSerializationType =
context.getFactoryConfiguration().get(KafkaDataSinkOptions.VALUE_FORMAT);
// 2. 创建工厂助手并验证配置
FactoryHelper helper = FactoryHelper.createFactoryHelper(this, context);
helper.validateExcept(
PROPERTIES_PREFIX, keyFormat.toString(), jsonSerializationType.toString());
// 3. 获取交付保证配置
DeliveryGuarantee deliveryGuarantee =
context.getFactoryConfiguration().get(KafkaDataSinkOptions.DELIVERY_GUARANTEE);
// 4. 设置时区
ZoneId zoneId = ZoneId.systemDefault();
if (!Objects.equals(
context.getPipelineConfiguration().get(PipelineOptions.PIPELINE_LOCAL_TIME_ZONE),
PipelineOptions.PIPELINE_LOCAL_TIME_ZONE.defaultValue())) {
zoneId = ZoneId.of(
context.getPipelineConfiguration()
.get(PipelineOptions.PIPELINE_LOCAL_TIME_ZONE));
}
// 5. 创建 Key 序列化器
SerializationSchema<Event> keySerialization =
KeySerializationFactory.createSerializationSchema(
helper.getFormatConfig(keyFormat.toString()), keyFormat, zoneId);
// 6. 创建 Value 序列化器
SerializationSchema<Event> valueSerialization =
ChangeLogJsonFormatFactory.createSerializationSchema(
helper.getFormatConfig(jsonSerializationType.toString()),
jsonSerializationType,
zoneId);
// 7. 提取 Kafka 属性
final Properties kafkaProperties = new Properties();
Map<String, String> allOptions = context.getFactoryConfiguration().toMap();
allOptions.keySet().stream()
.filter(key -> key.startsWith(PROPERTIES_PREFIX))
.forEach(
key -> {
final String value = allOptions.get(key);
final String subKey = key.substring((PROPERTIES_PREFIX).length());
kafkaProperties.put(subKey, value);
});
// 8. 获取其他配置参数
String topic = context.getFactoryConfiguration().get(KafkaDataSinkOptions.TOPIC);
boolean addTableToHeaderEnabled =
context.getFactoryConfiguration()
.get(KafkaDataSinkOptions.SINK_ADD_TABLEID_TO_HEADER_ENABLED);
String customHeaders =
context.getFactoryConfiguration().get(KafkaDataSinkOptions.SINK_CUSTOM_HEADER);
PartitionStrategy partitionStrategy =
context.getFactoryConfiguration().get(KafkaDataSinkOptions.PARTITION_STRATEGY);
String tableMapping = context.getFactoryConfiguration().get(SINK_TABLE_ID_TO_TOPIC_MAPPING);
// 9. 创建并返回 KafkaDataSink 实例
return new KafkaDataSink(
deliveryGuarantee,
kafkaProperties,
partitionStrategy,
zoneId,
keySerialization,
valueSerialization,
topic,
addTableToHeaderEnabled,
customHeaders,
tableMapping);
}
identifier() - 工厂标识符
java
@Override
public String identifier() {
return IDENTIFIER;
}
返回工厂的唯一标识符 "kafka",用于在配置中识别这个 Sink 类型。
requiredOptions() - 必需选项
java
@Override
public Set<ConfigOption<?>> requiredOptions() {
return new HashSet<>();
}
注意: 返回空集合,表示没有强制要求的配置选项。这使得配置非常灵活。
optionalOptions() - 可选选项
java
@Override
public Set<ConfigOption<?>> optionalOptions() {
Set<ConfigOption<?>> options = new HashSet<>();
options.add(KEY_FORMAT);
options.add(VALUE_FORMAT);
options.add(PARTITION_STRATEGY);
options.add(TOPIC);
options.add(SINK_ADD_TABLEID_TO_HEADER_ENABLED);
options.add(SINK_CUSTOM_HEADER);
options.add(KafkaDataSinkOptions.DELIVERY_GUARANTEE);
options.add(SINK_TABLE_ID_TO_TOPIC_MAPPING);
options.add(DEBEZIUM_JSON_INCLUDE_SCHEMA_ENABLED);
return options;
}
定义所有支持的可选配置选项。
关键技术细节
配置验证
java
helper.validateExcept(
PROPERTIES_PREFIX, keyFormat.toString(), jsonSerializationType.toString());
- 排除验证: 不验证以 properties. 开头的 Kafka 特定属性
- 排除格式相关配置: 不验证特定格式的配置项
时区处理
java
ZoneId zoneId = ZoneId.systemDefault();
if (!Objects.equals(
context.getPipelineConfiguration().get(PipelineOptions.PIPELINE_LOCAL_TIME_ZONE),
PipelineOptions.PIPELINE_LOCAL_TIME_ZONE.defaultValue())) {
zoneId = ZoneId.of(
context.getPipelineConfiguration()
.get(PipelineOptions.PIPELINE_LOCAL_TIME_ZONE));
}
逻辑: 如果管道配置中设置了时区,则使用该时区;否则使用系统默认时区。
Kafka 属性提取
java
final Properties kafkaProperties = new Properties();
Map<String, String> allOptions = context.getFactoryConfiguration().toMap();
allOptions.keySet().stream()
.filter(key -> key.startsWith(PROPERTIES_PREFIX))
.forEach(
key -> {
final String value = allOptions.get(key);
final String subKey = key.substring((PROPERTIES_PREFIX).length());
kafkaProperties.put(subKey, value);
});
功能: 提取所有以 properties. 开头的配置,并转换为 Kafka 生产者属性。
示例转换:
- properties.bootstrap.servers → bootstrap.servers
- properties.acks → acks
序列化器创建
java
// Key 序列化器
SerializationSchema<Event> keySerialization =
KeySerializationFactory.createSerializationSchema(
helper.getFormatConfig(keyFormat.toString()), keyFormat, zoneId);
// Value 序列化器
SerializationSchema<Event> valueSerialization =
ChangeLogJsonFormatFactory.createSerializationSchema(
helper.getFormatConfig(jsonSerializationType.toString()),
jsonSerializationType,
zoneId);
使用专门的工厂类创建序列化器,实现关注点分离。
配置使用示例
SQL 方式配置
java
CREATE TABLE kafka_sink (
-- 表结构定义
) WITH (
'connector' = 'kafka-cdc', -- 使用 kafka 标识符
'topic' = 'cdc-events',
'key.format' = 'json',
'value.format' = 'debezium-json',
'partition.strategy' = 'hash-by-key',
'sink.add-tableId-to-header-enabled' = 'true',
'properties.bootstrap.servers' = 'localhost:9092',
'properties.acks' = 'all'
);
编程方式配置
java
Configuration config = new Configuration();
config.set(KEY_FORMAT, KeyFormat.JSON);
config.set(VALUE_FORMAT, JsonSerializationType.DEBEZIUM_JSON);
config.set(TOPIC, "cdc-events");
config.set(SINK_ADD_TABLEID_TO_HEADER_ENABLED, true);
// Kafka 生产者属性
config.setString("properties.bootstrap.servers", "localhost:9092");
config.setString("properties.acks", "all");
DataSink sink = factory.createDataSink(context);
设计模式应用
工厂模式
- 封装复杂的对象创建逻辑
- 提供统一的创建接口
建造者模式(在 KafkaDataSink 中)
- 通过多个参数构建复杂对象
- 支持灵活的配置组合
策略模式
- 不同的序列化格式作为策略
- 不同的分区策略作为策略
总结
这个 KafkaDataSinkFactory 类:
- 作为创建入口: 负责创建配置完整的 KafkaDataSink 实例
- 处理复杂配置: 解析和验证各种配置选项
- 管理依赖创建: 创建序列化器、设置时区等
- 提供扩展性: 通过可选配置支持各种使用场景
- 保持灵活性: 没有强制配置要求,支持最小化配置
它是 Flink CDC Kafka 连接器的核心组件,将用户配置转换为可运行的 Sink 实例。