3. DataStream API基础:DataStream与DataSet的统一、Source算子、Transformation算子概览
3.1 批流一体:从DataSet到DataStream的演进
在Flink 1.12之前,批处理使用DataSet API,流处理使用DataStream API,两套API在底层执行引擎、调度策略和状态管理上存在显著差异。这种割裂导致用户需要为批和流分别编写两套代码,维护成本高,且难以实现真正的“同一份代码,两种运行模式”。
Flink 1.12及之后版本正式实现了批流一体:DataSet API被标记为软弃用,所有批处理作业统一使用DataStream API编写,通过将批数据视为有界流,在运行时由调度器自动选择批模式或流模式执行。这一统一带来了以下核心收益:
- API统一:批和流共用DataStream API,代码复用率提升80%以上。
- 语义一致:Exactly-Once、Watermark、状态后端等机制在批和流中行为一致。
- 运维简化:同一套监控、调优和部署流程覆盖所有作业。
| 对比维度 | DataSet API(旧) | DataStream API(统一后) |
|---|---|---|
| 数据模型 | 有界数据集 | 有界/无界统一数据流 |
| 执行模式 | 批模式(Stage-by-Stage) | 批模式 / 流模式自动切换 |
| 状态支持 | 有限(仅排序/聚合) | 完整状态后端(RocksDB / Heap) |
| 事件时间 | 不支持 | 完整Watermark + 事件时间语义 |
| 推荐状态 | 软弃用(Flink 1.18+) | 官方推荐 |
3.2 Source算子:数据接入的三种典型方式
Source算子是DataStream的起点,负责从外部系统读取数据并转换为DataStream<T>。本节聚焦三种最常用的Source:文件、Kafka和Socket,覆盖离线批处理与实时流处理的核心场景。
3.2.1 文件Source(有界流)
文件Source适用于读取HDFS、S3、本地文件系统上的静态数据,是批处理场景的主力。Flink提供了readFile和FileSource两种方式,推荐使用FileSource(基于Source API v2,支持目录监控和格式扩展)。
// 推荐方式:FileSource(支持批流统一)
DataStream<String> textStream = env.fromSource(
FileSource.forRecordStreamFormat(
new TextLineInputFormat(),
new Path("hdfs://cluster/data/events/2024/")
)
.monitorContinuously(Duration.ofHours(1)) // 流模式:每小时扫描新文件
.build(),
WatermarkStrategy.noWatermarks(),
"file-source"
);
// 传统方式:readFile(仅批模式)
DataStream<String> batchStream = env.readTextFile("file:///data/input/");
| 参数 | 说明 | 典型值 |
|---|---|---|
monitorContinuously |
启用持续监控模式,新文件到达后自动读取 | Duration.ofHours(1) |
FileEnumerator |
文件发现策略(递归/非递归、正则过滤) | RegexFileEnumerator |
SplitReader |
文件分片读取器(支持压缩格式) | TextLineInputFormat |
3.2.2 Kafka Source(无界流)
Kafka是实时特征工程中最常用的消息队列Source。Flink Kafka Connector支持精确一次语义、动态分区发现和消费偏移量管理。
Properties props = new Properties();
props.setProperty("bootstrap.servers", "kafka-broker:9092");
props.setProperty("group.id", "feature-group");
DataStream<String> kafkaStream = env.fromSource(
KafkaSource.<String>builder()
.setBootstrapServers("kafka-broker:9092")
.setTopics("user-click", "user-purchase")
.setGroupId("feature-group")
.setStartingOffsets(OffsetsInitializer.earliest())
.setValueOnlyDeserializer(new SimpleStringSchema())
.setProperty("commit.offsets.on.checkpoint", "true")
.build(),
WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofSeconds(5)),
"kafka-source"
);
3.2.3 Socket Source(测试/原型)
Socket Source仅用于本地开发和快速原型验证,不适合生产环境(无持久化、无容错)。
DataStream<String> socketStream = env.socketTextStream("localhost", 9999, "\n");
3.3 Transformation算子概览:特征工程的核心操作
Transformation算子将Source产生的原始数据流转换为特征数据流。以下按单流操作和多流操作分类,列出特征工程中最常用的算子。
3.3.1 单流Transformation
| 算子 | 输入 → 输出 | 特征工程典型用途 |
|---|---|---|
map |
1 → 1 | 字段提取、类型转换、缺失值填充 |
flatMap |
1 → 0..n | 日志解析(一行拆多条特征)、JSON展开 |
filter |
1 → 0/1 | 异常值过滤、无效样本剔除 |
keyBy |
DataStream → KeyedStream | 按用户ID/设备ID分组,为后续聚合做准备 |
reduce |
KeyedStream → DataStream | 滑动窗口内特征累加(如近1小时点击次数) |
process |
KeyedStream → DataStream | 自定义状态管理、定时器触发特征计算 |
3.3.2 多流Transformation
| 算子 | 输入 → 输出 | 特征工程典型用途 |
|---|---|---|
union |
多流 → 单流(同类型) | 合并多个数据源(如点击流+曝光流) |
connect / coMap |
多流 → 单流(不同类型) | 实时特征拼接(用户画像流 + 行为流) |
join |
多流 → 单流(窗口内关联) | 订单流与支付流关联,生成交易特征 |
intervalJoin |
多流 → 单流(时间区间关联) | 用户登录前5分钟内的行为特征提取 |
3.4 批流一体Source与Transformation架构图
下图展示了批流一体架构下,Source算子如何将不同数据源统一接入DataStream,再经过Transformation算子链生成特征数据流,最终输出到Sink的全过程。
3.5 实战要点与最佳实践
• 离线批处理(T+1特征)→ 文件Source + 批模式执行
• 实时流处理(毫秒级特征)→ Kafka Source + 流模式执行
• 原型验证 / 单元测试 → Socket Source + 本地环境