【免费下载链接】flink-learning
flink learning blog. http://www.54tianzhisheng.cn/ 含 Flink 入门、概念、原理、实战、性能调优、源码解析等内容。涉及 Flink Connector、Metrics、Library、DataStream API、Table API & SQL 等内容的学习案例,还有 Flink 落地应用的大型项目案例(PVUV、日志存储、百亿数据实时去重、监控告警)分享。欢迎大家支持我的专栏《大数据实时计算引擎 Flink 实战与性能优化》
项目地址:
https://gitcode.com/gh_mirrors/fl/flink-learning
点击查看 免费下载
本文是 flink-learning 仓库中「实时计算平台建设」模块的技术详解,围绕 flink-learning-project-real-time-computing-platform 模块的两份核心作业展开:以 Flink SQL DDL 为骨架的 FlinkSqlPlatformJob,以及以 Table API 编程为骨架的 TableApiExampleJob。读者通过本文可以掌握如何用 Flink SQL 与 Table API 两种方式搭建实时计算任务(Kafka 接入、Watermark 定义、滚动窗口聚合、UV 去重、动态表与流互转),并理解两条技术路线在实时计算平台中的分工与取舍。
一、模块定位:实时计算平台的两种提交范式
在真实的实时计算平台中,用户提交任务的方式通常分为两类:一类是数据分析师直接书写 SQL,交由平台解析执行;另一类是平台开发者用编程方式(Java/Scala)实现复杂业务逻辑。本模块正是围绕这两种范式给出的参考实现,代码位于:
- SQL 方式:FlinkSqlPlatformJob.java
- Table API 方式:TableApiExampleJob.java
两个作业都基于 StreamTableEnvironment(流表桥接环境)构建,它是连接 DataStream API 与 Table/SQL API 的关键入口。模块依赖关系见 pom.xml:除了共享的 flink-learning-project-common 模块外,还引入了 flink-table-api-java-bridge(provided 作用域,由运行环境提供)。
需要说明的是,本仓库根目录 pom.xml 将 flink.version 统一声明为 1.20.3、Kafka 连接器版本为 3.4.0-1.20、Java 编译目标为 11,因此下面展示的代码均以 Flink 1.20 系列的新版 API(如 KafkaSource、KafkaSink)为准。
二、模式一:FlinkSqlPlatformJob——纯 SQL 构建实时计算任务
FlinkSqlPlatformJob 以「订单实时统计」为业务场景,完整演示了从建表、声明 Watermark、执行窗口聚合到写出结果的 SQL 全流程,其执行链路为:
创建 StreamTableEnvironment → DDL 创建 Kafka Source 表 → DDL 创建 Sink 表 → 执行 INSERT INTO 聚合 SQL
2.1 创建流表桥接环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(4);
EnvironmentSettings settings = EnvironmentSettings.newInstance()
.inStreamingMode()
.build();
StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env, settings);
要点说明:
- env.setParallelism(4):设定作业整体并行度为 4,实际生产应依据 Kafka 分区数动态调整;
- EnvironmentSettings.newInstance().inStreamingMode():显式声明流处理模式(非批模式),这是流式实时计算平台的标准配置;
- StreamTableEnvironment.create(env, settings):将 DataStream 执行环境包装为流表环境,此后 DDL 与 SQL 都通过 tableEnv.executeSql(…) 提交。
2.2 用 DDL 定义 Kafka Source 表(含 Watermark 策略)
平台中数据源通常由平台侧统一注册,用户只需引用表名即可。本示例直接通过 SQL DDL 在作业内注册 ods_order 订单源表:
CREATE TABLE ods_order (
order_id STRING,
user_id STRING,
product_name STRING,
category STRING,
price DOUBLE,
quantity INT,
total_amount DOUBLE,
province STRING,
create_time BIGINT,
— 从 BIGINT 时间戳生成事件时间属性
ts AS TO_TIMESTAMP_LTZ(create_time, 3),
— 定义 Watermark:允许 5 秒乱序
WATERMARK FOR ts AS ts – INTERVAL '5' SECOND
) WITH (
'connector' = 'kafka',
'topic' = 'project-order-topic',
'properties.bootstrap.servers' = 'localhost:9092',
'properties.group.id' = 'sql-platform-group',
'scan.startup.mode' = 'latest-offset',
'format' = 'json',
'json.fail-on-missing-field' = 'false',
'json.ignore-parse-errors' = 'true'
);
这张 DDL 蕴含了实时计算平台中最常用的三类声明式能力:
2.3 用 DDL 定义 Print Sink 表(调试输出)
CREATE TABLE print_sink (
category STRING,
window_start TIMESTAMP(3),
window_end TIMESTAMP(3),
order_count BIGINT,
total_amount DOUBLE,
unique_users BIGINT
) WITH (
'connector' = 'print'
);
print 连接器会把每条结果打印到 TaskManager 的标准输出,适合平台开发阶段的联调验证;生产环境应替换为 Kafka Sink('connector' = 'kafka')或 JDBC/ClickHouse 等存储连接器。
2.4 核心聚合 SQL:TUMBLE 窗口 + COUNT DISTINCT UV 去重
INSERT INTO print_sink
SELECT
category,
TUMBLE_START(ts, INTERVAL '5' MINUTE) AS window_start,
TUMBLE_END(ts, INTERVAL '5' MINUTE) AS window_end,
COUNT(*) AS order_count,
SUM(total_amount) AS total_amount,
COUNT(DISTINCT user_id) AS unique_users
FROM ods_order
WHERE total_amount > 0
GROUP BY category, TUMBLE(ts, INTERVAL '5' MINUTE);
这段 SQL 汇聚了实时平台中三个高频知识点:
- TUMBLE 滚动窗口:TUMBLE(ts, INTERVAL '5' MINUTE) 按事件时间 ts 每 5 分钟切一个窗口,TUMBLE_START/TUMBLE_END 取出窗口起止时间;
- 多维窗口聚合:GROUP BY category, TUMBLE(…) 在按类别分桶的基础上再开窗口,输出每个类别每个窗口的统计结果;
- UV 去重:COUNT(DISTINCT user_id) 在窗口内统计去重用户数,等价于 UV 指标;COUNT(*) 与 SUM(total_amount) 则分别对应 PV/GMV 类指标。
执行 tableEnv.executeSql(aggregationSQL) 后返回 TableResult。源码注释明确指出:INSERT INTO 是异步执行的,流模式下作业会持续运行直到被取消,这是实时平台「常驻任务」的典型特征。
三、模式二:TableApiExampleJob——Table API 编程构建实时计算任务
TableApiExampleJob 以「页面访问行为分析」为业务场景,展示编程方式下的完整链路:KafkaSource 建流 → DataStream 转 Table → 编程式查询 → 动态表转回 DataStream。
3.1 从 Kafka 消费数据并生成带 Watermark 的 DataStream
KafkaSource<String> kafkaSource = ProjectKafkaUtil.buildKafkaStringSource(
ProjectConstants.TOPIC_PAGE_ACCESS, "table-api-example-group");
WatermarkStrategy<PageAccessEvent> watermarkStrategy = WatermarkStrategy
.<PageAccessEvent>forBoundedOutOfOrderness(Duration.ofSeconds(5))
.withTimestampAssigner(
(SerializableTimestampAssigner<PageAccessEvent>) (event, ts) ->
event.getTimestamp());
DataStream<PageAccessEvent> accessStream = env
.fromSource(kafkaSource, WatermarkStrategy.noWatermarks(), "Page Access Source")
.map(json -> GsonUtil.fromJson(json, PageAccessEvent.class))
.assignTimestampsAndWatermarks(watermarkStrategy);
源码细节印证:
- Kafka 连接器使用新版 KafkaSource API,具体构造逻辑封装在 ProjectKafkaUtil.java 的 buildKafkaStringSource 中:SimpleStringSchema 反序列化、OffsetsInitializer.latest() 最新位点起步、默认 broker 地址取 ProjectConstants.java 中的 localhost:9092;
- 消息先以 JSON 字符串形式消费,再通过 GsonUtil.fromJson 反序列化为 PageAccessEvent.java(含 userId、pageCategory、channel、stayDuration、timestamp、region 等字段,注释表明该模型服务于实时大屏 PV/UV 统计);
- Watermark 使用 forBoundedOutOfOrderness(Duration.ofSeconds(5)) 允许 5 秒乱序,这与 SQL 方式中 INTERVAL '5' SECOND 的乱序容忍度保持一致,说明两种模式在语义上是等价的。
3.2 DataStream → Table:fromDataStream 字段映射
Table accessTable = tableEnv.fromDataStream(accessStream,
$("eventId"),
$("userId"),
$("pageUrl"),
$("pageTitle"),
$("pageCategory"),
$("channel"),
$("stayDuration"),
$("timestamp").as("ts"), // 重命名事件时间字段
$("deviceType"),
$("region"));
fromDataStream 把带类型信息的 DataStream 转换为 Table,并显式声明字段顺序;$("timestamp").as("ts") 是 Expression API 的字段重命名,为后续窗口聚合准备好事件时间字段 ts。
3.3 编程式查询:filter + select + groupBy + 聚合
Table filteredTable = accessTable
.filter($("channel").isNotNull())
.select($("pageCategory"), $("channel"), $("userId"), $("stayDuration"), $("region"));
Table channelStats = filteredTable
.groupBy($("channel"))
.select(
$("channel"),
$("userId").count().as("pv"),
$("stayDuration").avg().as("avg_stay")
);
- filter($("channel").isNotNull()) 过滤掉渠道为空的数据;
- $("userId").count() 统计各渠道访问次数(近似 PV),$("stayDuration").avg() 计算平均停留时长;
- 整条查询全部通过静态导入的 $ / lit 表达式构建,这正是 README 中「Expression API:$("field") 字段引用」知识点的落地。
3.4 Table → DataStream:toChangelogStream 输出结果
DataStream<Row> resultStream = tableEnv.toChangelogStream(channelStats);
resultStream.print("channel-stats");
env.execute("Table API 编程示例");
由于 channelStats 是带更新的聚合结果(动态表),转回 DataStream 必须使用 toChangelogStream(而非 toAppendStream),得到的是包含 +I/-U/+U 等变更类型的 Changelog 流,打印输出即为流式更新语义下的渠道统计结果。这一步也是 README 中「Changelog 流:动态表转回 DataStream」的代码级体现。
四、Flink SQL vs Table API:选型对比
README 给出了平台侧最关心的对比维度,结合两个作业的源码实现可以进一步明确各自的适用边界:
| 使用方式 | 纯 SQL 字符串 | Java/Scala 编程 |
| 适用人群 | 数据分析师 | 平台开发者 |
| 灵活性 | 中等 | 高 |
| 可调试性 | 低 | 高 |
| 动态提交 | 容易 | 较难 |
从实现上看:
- SQL 方式(FlinkSqlPlatformJob):建表、窗口、聚合全部是字符串,天然适合做成「SQL 即任务」的平台化提交——平台接收用户 SQL 后动态拼接执行即可,无需重新编译打包,这是实时计算平台最常见的形态;
- Table API 方式(TableApiExampleJob):逻辑以代码表达,可与 DataStream API 自由互转(fromDataStream/toChangelogStream),便于复用 ProjectKafkaUtil、GsonUtil 等既有工具链,适合业务逻辑复杂、需要精细控制(如自定义 Watermark 分配、旁路输出)的开发场景;
- 两者的底层执行引擎与优化器是同一套,区别只在「表达层」,因此同一业务用两种方式书写,执行语义一致。
五、涉及的核心 Flink 知识点速览
README 中的知识点表与上述两个作业一一对应,汇总如下:
| StreamTableEnvironment | 流表桥接环境 | 全部 |
| SQL DDL | 用 SQL 定义表结构和连接器 | FlinkSqlPlatformJob |
| Kafka Connector DDL | SQL 中配置 Kafka 连接器 | FlinkSqlPlatformJob |
| TUMBLE Window | SQL 滚动窗口函数 | FlinkSqlPlatformJob |
| COUNT DISTINCT | SQL UV 去重 | FlinkSqlPlatformJob |
| fromDataStream | DataStream 转 Table | TableApiExampleJob |
| toChangelogStream | Table 转 DataStream | TableApiExampleJob |
| Expression API | $("field") 字段引用 | TableApiExampleJob |
六、如何运行本模块
仓库为多模块 Maven 工程,模块间存在依赖关系(本模块依赖 flink-learning-project-common),建议在仓库根目录先构建共享模块,再运行目标作业:
# 在仓库根目录下编译项目公共模块
mvn -pl flink-learning-project/flink-learning-project-common -am install -DskipTests
# 编译实时计算平台模块
mvn -pl flink-learning-project/flink-learning-project-real-time-computing-platform -am package -DskipTests
运行前提:
七、小结:从示例到平台化落地
本模块的价值不在于单个作业,而在于示范了实时计算平台的两条核心能力路径:SQL 化动态提交(DDL + INSERT INTO,支撑分析师自助取数与指标计算)与 编程化任务开发(DataStream ↔ Table 互转,支撑平台开发者实现复杂逻辑)。结合仓库中 flink-learning-project-real-time-data-warehouse、flink-learning-project-real-time-computing-platform 等兄弟模块可以看到,这类 SQL/Table 双模式模板正是整个项目体系中「数仓分层、指标计算、平台建设」公共底座的一部分。开发者可在此基础上,将示例中的 Print Sink 替换为真实存储连接器、将常量提取到配置中心,即可快速演化出可上线的实时计算平台作业。
赞
【免费下载链接】flink-learning
flink learning blog. http://www.54tianzhisheng.cn/ 含 Flink 入门、概念、原理、实战、性能调优、源码解析等内容。涉及 Flink Connector、Metrics、Library、DataStream API、Table API & SQL 等内容的学习案例,还有 Flink 落地应用的大型项目案例(PVUV、日志存储、百亿数据实时去重、监控告警)分享。欢迎大家支持我的专栏《大数据实时计算引擎 Flink 实战与性能优化》
项目地址:
https://gitcode.com/gh_mirrors/fl/flink-learning
点击查看 免费下载
相关推荐
深度解析Fay AI Agent框架:构建智能数字人系统的技术实现方案
Node.js 生产环境最佳实践:正确设置 NODE_ENV=production 提升性能
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考




