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

相关推荐
架构师老Y4 小时前
011、消息队列应用:RabbitMQ、Kafka与Celery
python·架构·kafka·rabbitmq·ruby
talen_hx2968 小时前
《kafka核心源码解读》学习笔记 Day 02
笔记·学习·kafka
lifallen8 小时前
如何保证 Kafka 的消息顺序性?
java·大数据·分布式·kafka
真实的菜8 小时前
Kafka 2.x vs 3.x,我为什么选择升级?
kafka
时光追逐者8 小时前
分享四款开源且实用的 Kafka 管理工具
分布式·kafka·开源
Rick19938 小时前
rabbitmq, rocketmq, kafka这三种消息如何分别保住可靠性,顺序性,以及应用场景?
kafka·rabbitmq·rocketmq
☞遠航☜10 小时前
kafka快速上手
分布式·kafka·linq
工具罗某人21 小时前
docker compose部署kafka集群搭建
docker·容器·kafka
qq_297574671 天前
【Kafka 系列・入门第六篇】Kafka 集群部署(3 节点)+ 负载均衡配置
分布式·kafka·负载均衡