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 实例。

相关推荐
太阳伞下的阿呆11 小时前
kafka高吞吐持久化方案(2)
分布式·kafka·高并发·重入锁
Chasing__Dreams18 小时前
kafka--基础知识点--19--消息重复
分布式·kafka
import_random2 天前
[kafka]伪集群搭建,各个节点配置文件中listeners参数的配置
kafka
Mr.朱鹏2 天前
SQL深度分页问题案例实战
java·数据库·spring boot·sql·spring·spring cloud·kafka
山沐与山3 天前
【MQ】Kafka与RocketMQ深度对比
分布式·kafka·rocketmq
yumgpkpm3 天前
Cloudera CDP7、CDH5、CDH6 在华为鲲鹏 ARM 麒麟KylinOS做到无缝切换平缓迁移过程
大数据·arm开发·华为·flink·spark·kafka·cloudera
树下水月3 天前
Easyoole 使用rdkafka 进行kafka的创建topic创建 删除 以及数据发布 订阅
分布式·kafka
Cat God 0073 天前
基于Docker搭建kafka集群
docker·容器·kafka
Cat God 0073 天前
基于 Docker 部署 Kafka(KRaft + SASL/PLAIN 认证)
docker·容器·kafka