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

相关推荐
最笨的羊羊2 小时前
Flink CDC系列之:Kafka 分区策略枚举类PartitionStrategy
partition·flink cdc系列·strategy·枚举类·kafka 分区策略
s***46982 小时前
Spring Boot集成Kafka:最佳实践与详细指南
spring boot·kafka·linq
最笨的羊羊5 小时前
Flink CDC系列之:Kafka表结构信息管理类TableSchemaInfo
flink cdc系列·kafka表结构信息·管理类·tableschemainfo
Rust语言中文社区7 小时前
【Rust日报】 walrus:分布式消息流平台,比 Kafka 快
开发语言·分布式·后端·rust·kafka
最笨的羊羊10 小时前
Flink CDC系列之: Kafka 数据接收器实现类KafkaDataSink
kafka·flink cdc系列·数据接收器实现类·kafkadatasink
大迪吃小迪10 小时前
Kafka 技术问答总结文档(与RocketMQ对比)
分布式·kafka
杀死那个蝈坦10 小时前
UV 统计(独立访客统计)
java·jvm·spring·kafka·tomcat·maven
z***565611 小时前
Spring Boot集成Kafka:最佳实践与详细指南
spring boot·kafka·linq
9***Y4821 小时前
后端在分布式中的Apache Kafka
分布式·kafka