Flink Connector Formats深度解析:从原理到实践

一、引言

在实时数据处理管道中,数据的序列化与反序列化(SerDe)是连接外部系统的关键环节。Apache Flink 通过Connector Formats机制,将数据编解码逻辑从连接器中解耦,实现了格式与连接器的灵活组合。

复制代码
┌──────────────┐       ┌──────────────┐       ┌──────────────┐
│  数据源系统   │──────▶│  Flink 作业   │──────▶│  目标系统     │
│ (Kafka/MQ)   │ bytes │  (处理逻辑)   │ bytes │ (ES/DB/HDFS) │
└──────────────┘       └──────────────┘       └──────────────┘
        │                      │                       │
        ▼                      ▼                       ▼
   JSON/Avro/CSV         内部 RowData            JSON/Parquet
   Protobuf...           类型系统               Avro/Canal...

外部系统中的数据以多种编码格式存储和传输(JSON、Avro、Protobuf、CSV、Parquet 等),Flink 作业在读取和写出时,必须完成:

  • 反序列化(Deserialization):将 byte\[\] 解析为 Flink 内部数据结构。
  • 序列化(Serialization):将 Flink 内部数据结构编码为 byte\[\] 。

Flink 采用Connector + Format 分层架构,核心设计原则:

|--------|----------------------------------------------------------|
| 设计原则 | 说明 |
| 关注点分离 | Connector 负责与外部系统的 I/O 交互,Format 负责数据编解码 |
| 组合灵活性 | 同一 Connector 可搭配不同 Format(如 Kafka + JSON / Kafka + Avro) |
| 可扩展性 | 用户可自定义 Format 而无需修改 Connector |
| API 统一 | Table/SQL 层通过 SPI 机制自动发现并加载 Format |

二、核心机制原理

1.整体架构

2.DataStream API 中的 Format 机制

在 DataStream API 中,Format 的核心接口:

复制代码
// 反序列化接口
public interface DeserializationSchema<T> extends Serializable, ResultTypeQueryable<T> {
    T deserialize(byte[] message) throws IOException;
    default void open(InitializationContext context) throws Exception {}
    boolean isEndOfStream(T nextElement);
}

// 序列化接口
public interface SerializationSchema<T> extends Serializable {
    byte[] serialize(T element);
    default void open(InitializationContext context) throws Exception {}
}

3.Table/SQL API 中的 Format 机制

Table API 层引入了更高级的抽象:

复制代码
// Table API 反序列化格式工厂
public interface DeserializationFormatFactory extends FormatFactory {
    DecodingFormat<DeserializationSchema<RowData>> createDecodingFormat(
        DynamicTableFactory.Context context,
        ReadableConfig formatOptions);
}

// Table API 序列化格式工厂
public interface SerializationFormatFactory extends FormatFactory {
    EncodingFormat<SerializationSchema<RowData>> createEncodingFormat(
        DynamicTableFactory.Context context,
        ReadableConfig formatOptions);
}

4.Format 与 Connector 的绑定关系

并非所有 Format 都能与所有 Connector 搭配。以下是常见的兼容矩阵:

|---------------|-------|------------|------|---------------|--------------|
| Format | Kafka | Filesystem | JDBC | Elasticsearch | Upsert-Kafka |
| JSON | ✅ | ✅ | ❌ | ✅ | ✅ |
| Avro | ✅ | ✅ | ❌ | ❌ | ✅ |
| CSV | ✅ | ✅ | ❌ | ❌ | ✅ |
| Parquet | ❌ | ✅ | ❌ | ❌ | ❌ |
| ORC | ❌ | ✅ | ❌ | ❌ | ❌ |
| Canal-JSON | ✅ | ❌ | ❌ | ❌ | ❌ |
| Debezium-JSON | ✅ | ❌ | ❌ | ❌ | ❌ |
| Maxwell-JSON | ✅ | ❌ | ❌ | ❌ | ❌ |
| Raw | ✅ | ✅ | ❌ | ❌ | ✅ |
| Protobuf | ✅ | ✅ | ❌ | ❌ | ✅ |

1.JSON Format

Table API DDL 配置示例:

复制代码
CREATE TABLE kafka_source (
    user_id BIGINT,
    user_name STRING,
    event_time TIMESTAMP(3),
    payload ROW<action STRING, amount DECIMAL(10,2)>
) WITH (
    'connector' = 'kafka',
    'topic' = 'user_events',
    'properties.bootstrap.servers' = 'kafka:9092',
    'properties.group.id' = 'flink-consumer',
    'scan.startup.mode' = 'latest-offset',
    
    -- Format 配置
    'format' = 'json',
    'json.fail-on-missing-field' = 'false',
    'json.ignore-parse-errors' = 'true',
    'json.timestamp-format.standard' = 'SQL',
    'json.map-null-key.mode' = 'DROP',
    'json.encode.decimal-as-plain-number' = 'true'
);

关键配置参数说明:

|-------------------------------------|-------|----------------------------------------|
| 参数 | 默认值 | 说明 |
| json.fail-on-missing-field | false | JSON 中缺失声明字段时是否失败 |
| json.ignore-parse-errors | false | 解析错误时是否跳过(而非抛异常) |
| json.timestamp-format.standard | SQL | 时间戳格式:SQL 或 ISO-8601 |
| json.map-null-key.mode | FAIL | Map 中 key 为 null 的处理:FAIL/DROP/LITERAL |
| json.encode.decimal-as-plain-number | false | DECIMAL 类型是否以非科学计数法输出 |

DataStream API 使用示例:

复制代码
// 使用 JsonDeserializationSchema(需要 POJO 或指定 TypeInformation)
KafkaSource<Event> source = KafkaSource.<Event>builder()
    .setBootstrapServers("kafka:9092")
    .setTopics("user_events")
    .setGroupId("flink-consumer")
    .setStartingOffsets(OffsetsInitializer.latest())
    .setDeserializer(new JsonDeserializationSchema<>(Event.class))
    .build();

// 或使用 JSONKeyValueDeserializationSchema 获取元数据
KafkaSource<ObjectNode> source = KafkaSource.<ObjectNode>builder()
    .setBootstrapServers("kafka:9092")
    .setTopics("user_events")
    .setGroupId("flink-consumer")
    .setDeserializer(new JSONKeyValueDeserializationSchema(true))
    .build();

2.Avro Format

Table API 示例:

复制代码
CREATE TABLE kafka_avro_source (
    user_id BIGINT,
    user_name STRING,
    email STRING,
    created_at TIMESTAMP(3)
) WITH (
    'connector' = 'kafka',
    'topic' = 'user_avro',
    'properties.bootstrap.servers' = 'kafka:9092',
    
    -- Avro Confluent Format
    'format' = 'avro-confluent',
    'avro-confluent.url' = 'http://schema-registry:8081',
    'avro-confluent.schema-registry.subject' = 'user_avro-value'
);

DataStream API 示例:

复制代码
// 使用 Avro SpecificRecord
KafkaSource<UserEvent> source = KafkaSource.<UserEvent>builder()
    .setBootstrapServers("kafka:9092")
    .setTopics("user_avro")
    .setGroupId("flink-consumer")
    .setDeserializer(
        KafkaRecordDeserializationSchema.valueOnly(
            ConfluentRegistryAvroDeserializationSchema.forSpecific(
                UserEvent.class,
                "http://schema-registry:8081"
            )
        )
    )
    .build();

3.CSV Format

复制代码
CREATE TABLE csv_source (
    order_id BIGINT,
    product STRING,
    quantity INT,
    price DECIMAL(10,2)
) WITH (
    'connector' = 'kafka',
    'topic' = 'orders_csv',
    'properties.bootstrap.servers' = 'kafka:9092',
    
    'format' = 'csv',
    'csv.field-delimiter' = ',',
    'csv.disable-quote-character' = 'false',
    'csv.quote-character' = '"',
    'csv.allow-comments' = 'true',
    'csv.ignore-parse-errors' = 'true',
    'csv.null-literal' = 'NULL'
);

4.Debezium-JSON Format(CDC 场景)

复制代码
CREATE TABLE mysql_cdc_source (
    id INT,
    name STRING,
    status STRING,
    update_time TIMESTAMP(3),
    PRIMARY KEY (id) NOT ENFORCED
) WITH (
    'connector' = 'kafka',
    'topic' = 'dbserver1.mydb.users',
    'properties.bootstrap.servers' = 'kafka:9092',
    
    'format' = 'debezium-json',
    'debezium-json.schema-include' = 'true',
    'debezium-json.ignore-parse-errors' = 'false',
    'debezium-json.timestamp-format.standard' = 'ISO-8601'
);

CDC Format 的 changelog 语义:

5.Protobuf Format

复制代码
CREATE TABLE proto_source (
    user_id INT,
    user_name STRING,
    email STRING
) WITH (
    'connector' = 'kafka',
    'topic' = 'proto_users',
    'properties.bootstrap.servers' = 'kafka:9092',
    
    'format' = 'protobuf',
    'protobuf.message-class-name' = 'com.example.proto.UserProto$User',
    'protobuf.ignore-parse-errors' = 'true',
    'protobuf.read-default-values' = 'true'
);

6.Raw Format

复制代码
CREATE TABLE raw_source (
    message STRING
) WITH (
    'connector' = 'kafka',
    'topic' = 'raw_topic',
    'properties.bootstrap.servers' = 'kafka:9092',
    
    'format' = 'raw',
    'raw.charset' = 'UTF-8',
    'raw.endianness' = 'big-endian'
);
相关推荐
2601_960356381 小时前
大数据相关专业课程难度排名分析
大数据
让学习成为一种生活方式1 小时前
samblaster v.0.1.26安装与使用--生信工具096
大数据·elasticsearch·搜索引擎
紫昂张2 小时前
RFM模型与用户分群
大数据
财经资讯数据_灵砚智能2 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(夜间-次晨)2026年6月15日
大数据·人工智能·python·ai·信息可视化·自然语言处理·灵砚智能
大大大大晴天2 小时前
Flink Connector Formats深度解析:从原理到实践
flink
天行健,君子而铎2 小时前
自适应分类·高准确率·可视化易用——运营商数据分类分级解决方案
大数据·分类
root_102 小时前
kylin-v10-sp3-x86系统安装vmware-17
大数据·chrome·kylin
小t说说2 小时前
技术观察:从职坐标看一家IT培训机构的课程体系与AI教学工具
大数据·人工智能
超级赛博搬砖工2 小时前
SEO代理解析:成功搜索引擎抓取你需要了解的事项
大数据·运维·服务器·网络