1.序篇-先说结论
博主希望你在看完本文后一定要养成这个编程习惯:使用 DataStream API 实现 Flink 任务时,Watermark Assigner 能靠近 Source 节点就靠近 Source 节点,尽量前置。
要想问为啥,接着往下看!!!
我从以下几个章节说明上述的问题以及为什么这样建议,希望能抛砖引玉,带给大家一些启发。
⭐ 踩坑场景篇-这个坑是啥样的
⭐ 问题排查篇-坑的排查过程
⭐ 问题原理解析篇-导致问题的机制是什么
⭐ 避坑篇-如何避免这种问题
⭐ 总结篇
2.踩坑场景篇-这个坑是啥样的
2.1.需求场景
首先介绍一下这个坑对应的一个需求场景以及第一版本的实现代码。
需求:在电商平台中,需要根据网页在线用户的心跳日志(每 30s 上报一次用户心跳日志)计算当前这一分钟在购物车页面(Shopping-Cart)停留的在线人数。
数据源:每 30s 上报一次的用户心跳日志(user_id、page、time 三个字段分别对应 用户 id、用户所在页面、日志上报时间)
数据处理:先过滤出购物车按照时间戳对用户心跳日志进行滚动窗口(Tumble)聚合计算
数据汇:每分钟聚合的结果数据(uv、time 两个字段分别对应 购物车页面的当前这一分钟的同时在线人数、当前这一分钟的时间戳)
Flink DataStream API 具体实现代码如下:
public class WatermarkTest {
public static void main(String[] args) throws Exception {
// 获取到 Flink 环境,博主自己封装的接口 FlinkEnv
FlinkEnv flinkEnv = FlinkEnvUtils.getStreamTableEnv(args);
// 设置并发度
flinkEnv.env().setParallelism(100);
flinkEnv.env()
// 数据源:上报的日志
.addSource(xxx)
// 过滤出 购物车页面(Shopping-Cart)的数据
.filter(new FilterFunction<SourceModel>() {
@Override
public boolean filter(SourceModel value) throws Exception {
return value.getPage().equals("Shopping-Cart");
}
})
// 分配 Watermark
.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor<SourceModel>(Time.minutes(1)) {
@Override
public long extractTimestamp(SourceModel element) {
return element.getTime();
}
})
// 为了进行合并计算,shuffle 到一个算子中,所以此处返回结果固定为 0
.keyBy(new KeySelector<SourceModel, Long>() {