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'
);
相关推荐
蜡笔削薪16 分钟前
财联万业(杭州)数字科技有限公司能否给代理划定独家经营区域?
大数据·人工智能·python·科技
动恰客流统计34 分钟前
零食集合店爆火背后:客流统计技术如何重构新零售运营决策
大数据·人工智能
B8017913Y3 小时前
2026撰写行业报告总写不完零散专业词汇?可系统梳理行业专业词汇
大数据·人工智能
2601_962341303 小时前
计算机毕业设计之jsp考研在线复习平台
java·大数据·开发语言·hadoop·python·考研·课程设计
2503_931712484 小时前
10m/s超高速电梯:西奥XO-NEWIII如何树立行业速度标杆
java·大数据·数据库
@insist1234 小时前
系统规划与管理师-体系结构/接口/数据/软件定义全解析
大数据·软考·系统规划与管理师·软件水平考试·系统规划与管理工程师
新e选店长4 小时前
新e选烤火罩使用相关问题、购买相关问题、售后相关问题。
大数据·科技·物联网·材质·烤火罩
法雅特吉他4 小时前
国产吉他品牌技术发展与市场格局分析
大数据·经验分享·新媒体运营·学习方法·流量运营·材质·内容运营
我的温馨家园5 小时前
新手入局干细胞研究的五大认知陷阱
大数据·人工智能·精选
小羊Yveesss5 小时前
2026年外贸建站服务器怎么选?访问速度、稳定性和海外收录怎么判断
大数据·运维·服务器