3. Flink编程模型:DataStream API与DataSet API的演进

Flink 从诞生至今,其编程模型经历了从“两套 API 并行”到“流批一体”的重大演进。理解这一演进过程,是掌握 Flink 程序骨架(Source、Transformation、Sink)的前提。本章将梳理 DataStream API 与 DataSet API 的差异、合并历程,并给出 Flink 程序的标准结构。

3.1 编程模型演进概览

Flink 最初为批处理和流处理分别设计了不同的 API:

  • DataSet API:面向有界数据集(批处理),基于“数据集”抽象,操作如 map、reduce、groupBy,执行时采用批调度策略。
  • DataStream API:面向无界数据流(流处理),基于“数据流”抽象,操作如 keyBy、window、process,执行时采用流调度策略。

从 Flink 1.12 开始,官方正式推进“流批一体”,将 DataSet API 逐步合并到 DataStream API 中。当前推荐统一使用 DataStream API 编写所有作业,通过设置执行模式(STREAMING / BATCH)来切换流/批语义。

维度 DataSet API(旧) DataStream API(推荐)
数据模型 有界数据集 无界/有界数据流
执行模式 批模式 流模式 / 批模式
窗口支持 有限(基于分组) 丰富(时间窗口、计数窗口、会话窗口等)
状态管理 无原生状态 Keyed State / Operator State
事件时间 不支持 原生支持(Watermark、Timestamp)
当前状态 已标记为废弃(Flink 1.18+) 主推 API,持续演进
学习建议: 新项目请直接使用 DataStream API。即使处理静态文件,也通过 env.setRuntimeMode(RuntimeExecutionMode.BATCH) 启用批模式,无需再引入 DataSet API。

3.2 Flink程序基本骨架:Source → Transformation → Sink

无论流还是批,Flink 程序都遵循统一的“三段式”骨架:

  1. Source(数据源):从外部系统读取数据,如 Kafka、文件、Socket、自定义 Source。
  2. Transformation(转换算子):对数据进行处理,如过滤、映射、聚合、开窗、连接等。
  3. Sink(数据输出):将结果写入外部系统,如 Kafka、Elasticsearch、文件、数据库、自定义 Sink。
核心公式: Flink 作业 = Source + Transformation 链 + Sink。所有算子通过 DataStream 串联,形成 DAG(有向无环图)。

3.3 程序骨架的SVG流程图

Flink程序骨架:Source → Transformation → Sink Flink 程序骨架(DataStream API) Source 数据源(Kafka/文件/…) Transformation map / filter / window / … Sink 输出(Kafka/ES/…) 说明 Source 产生 DataStream[T] → Transformation 返回新的 DataStream[T] → Sink 消费并输出 所有算子延迟执行,需调用 env.execute() 触发作业提交

3.4 完整代码示例(流批一体)

以下示例展示一个完整的 Flink 程序,从 Socket 读取文本,进行词频统计,输出到控制台:

import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.util.Collector;

public class WordCountStreaming {
    public static void main(String[] args) throws Exception {
        // 1. 创建执行环境(流批一体)
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        // 可选:设置为批模式
        // env.setRuntimeMode(RuntimeExecutionMode.BATCH);

        // 2. Source:从 Socket 读取文本流
        DataStream<String> textStream = env.socketTextStream("localhost", 9999);

        // 3. Transformation:分词、映射为 (word, 1)、分组求和
        DataStream<Tuple2<String, Integer>> wordCounts = textStream
            .flatMap(new FlatMapFunction<String, Tuple2<String, Integer>>() {
                @Override
                public void flatMap(String line, Collector<Tuple2<String, Integer>> out) {
                    for (String word : line.split("\\s+")) {
                        out.collect(new Tuple2<>(word, 1));
                    }
                }
            })
            .keyBy(value -> value.f0)
            .sum(1);

        // 4. Sink:打印到控制台
        wordCounts.print();

        // 5. 执行作业
        env.execute("Flink WordCount Streaming");
    }
}
注意: 上述代码中 keyBy 使用 Lambda 表达式(value -> value.f0),要求 Java 8+。如果使用 DataSet API 的 groupBy,在 Flink 1.18+ 中会触发废弃警告,请迁移至 DataStream API。

3.5 常用Source与Sink对照表

类别 Source 示例 Sink 示例
消息队列 KafkaSource、RabbitMQ KafkaSink、PulsarSink
文件系统 FileSource(TextLineFormat) FileSink(Bulk / Streaming)
数据库 JDBCSource(Table API) JDBCSink、ElasticsearchSink
网络 SocketTextStream SocketSink(较少用)
自定义 实现 SourceFunction / ParallelSourceFunction 实现 SinkFunction

3.6 执行模式对比

Flink 支持三种执行模式,通过 RuntimeExecutionMode 指定:

模式 适用场景 特点
STREAMING 无界流(实时) 默认模式,逐条处理,支持事件时间、Watermark、Checkpoint
BATCH 有界数据(批处理) 优化吞吐,按批次调度,不支持事件时间/Watermark(自动退化)
AUTOMATIC 自动推断 根据 Source 是否有界自动选择,有界则 BATCH,无界则 STREAMING
最佳实践: 在开发阶段使用 AUTOMATIC 模式,让 Flink 自动判断;生产环境明确指定 STREAMING 或 BATCH,避免因 Source 变更导致执行模式意外切换。

3.7 演进总结:从两套API到统一DataStream

Flink API演进时间线 Flink API 演进:从两套到统一 DataSet API DataStream API 流批一体 DataStream Flink 1.0 Flink 1.12 Flink 1.18+ DataSet API 已废弃,新项目统一使用 DataStream API + RuntimeExecutionMode

3.8 本章要点

  • API选择:始终使用 DataStream API,通过 RuntimeExecutionMode 切换流/批。
  • 程序骨架:Source → Transformation → Sink,三者缺一不可。
  • 延迟执行:所有算子构建 DAG,调用 env.execute() 后才真正提交作业。
  • Source/Sink 生态:Flink 提供丰富的连接器(Kafka、文件、JDBC、ES 等),也支持自定义。
  • 演进方向:流批一体是 Flink 的核心优势,后续章节将深入 DataStream API 的各类算子与状态管理。