Window API
流数据来了之后需要对它按照时间窗口进行划分,其中window可以看作桶。过来的数据流向一个个桶里面。
Window的两个操作构成:一个是设置窗口.window(keyby之后);一个是窗口计算函数(reduce,aggregate,apply,process)
设置窗口:
-
- 时间窗口(按照时间进行划分)
- 计数窗口(按照过来的数据数量进行划分)
-
- 滚动窗口(例如:.timeWidow(Time.second(15)))
![c6e6c8323396031944ee520c19452e40.png](https://i-blog.csdnimg.cn/blog_migrate/1d797a76f139f26564c3bb553cc0bb7d.jpeg)
-
- 滑动窗口(例如:.timeWindow(Time.seconds(15), Time.seconds(5)))
![f9289150042d20f1baa16113b6ef0118.png](https://i-blog.csdnimg.cn/blog_migrate/8290a9f87f3e018486181a0ed31a59bc.jpeg)
-
- 会话窗口(指定间隔时间session gap超过某个值)
![cd98be913339125641f388f14caaa751.png](https://i-blog.csdnimg.cn/blog_migrate/b478dd56f899fdc4f3c55bf9b5f25d21.jpeg)
窗口计算函数:
-
- 增量聚合函数:ReduceFunction,AggregateFunction;流式处理过程;(对每条数据到来进行计算,保持一个中间状态。)
- 全窗口函数:WindowFunction(.apply()方法里),ProcessWindowFunction,类似批处理。(先把窗口的数据收集起来,到点计算,等到窗口关闭则开始输出。)可以做更多操作。
其他API:
.trigger()----触发器
实践中注意:
1,遇到了统计一天用户购买量的场景,flink默认的时间是8:00AM--次日8:00AM。需要设置偏移量。
- window( TumblingProcessingTimeWindows.of(Time.days(1), Time.hours(-8)) ) //设置偏移量。时区转换。
2,传入的数据流比较稀疏,用滑动窗口会重复输出值。(某个元素属于多个滑动窗口)
3,会话窗口可以获取用户一段实践的行为,可以将其运用在实时推荐。
时间语义及实现:
数据有两种时间语义:(我们一般更加关注EventTime)
- EventTime:数据产生时间。
- ProcessonTime:数据处理时间。
在Flink代码设置环境的时候我们会设置时间语义:
- env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
WaterMark
为什么我们需要WaterMark?
![7f7539dda7e5e0c2fcf794404f6e30e3.png](https://i-blog.csdnimg.cn/blog_migrate/5960fa9453d25e7d1a4881ced46c641d.jpeg)
因为分布式,网络等原因导致数据产生到Flink收到数据会有乱序。如果按照eventTime来决定窗口内的数据是否运行,就不能明确数据是否全部都来了。因为我们是实时处理,也不能无限期等下去。为了处理乱序数据流,需要有一个机制来保证达到一个特定时间就触发window计算。
PS:处理乱序数据,Flink有三重保证:
- watermark可以设置延迟时间
- window的allowedLateness方法,可以设置窗口允许处理迟到数据的时间
- window的sideOutputLateData方法,可以将迟到的数据写入侧输出流
WaterMark如何来解决这个问题?
WaterMark核心思想(多等一下):数据流中包含时间戳,我们遇到一个时间戳达到了窗口关闭时间,不应该立刻触发窗口计算,而是等待一段时间,等迟到的数据来了再关闭窗口。
waterMark实现:waterMark实际上是一条特殊的数据记录,插入到数据流中。
Watermark = 当前Timestep最大时间戳 - 延迟时间
Watermark 用于表示 timestamp 小于 Watermark 的数据,都已经到达了。之后就不会再来时间戳比watermark里面的数值小的数据了。因此,window 的计算也是由Watermark 触发的。
![4d1e1a86309081a7592fb7ca04ed58fb.png](https://i-blog.csdnimg.cn/blog_migrate/65212e36ccc3418fcf730b579915c644.png)
WaterMark设置比较大的延迟,对应的准确性得到保证,但是实时性减少。具体在应用过程中根据情况来设置。
WaterMark传递
![27189108014c0d5d4b48dd3123e475bf.png](https://i-blog.csdnimg.cn/blog_migrate/967be05e13c8abaaa119daf4326068d5.jpeg)
在分布式系统中Task计算节点存在多个输入和多个输出的情况。对应的WaterMark分配原则有两种:
- 多流输出(图:2,4):通过广播的形式传播下去。
- 多流输出(图:1,3):先创建一个分区的WM,选择最小的watermark(他之前的数据都到齐了)
WaterMark代码引入
一般的有两个操作:提取时间戳字段和设置延迟时间。
- 比较稀疏的话,用断点式(AssignerWithPunctuatedWatermarks)生成WaterMark好一点,其中默认是200ms。
- 对于比较密集的话用周期性(AssignerWithPeriodicWatermarks)生成WaterMark,断点式比较费性能。
- AssignerWithPeriodicWatermarks(周期性生成WM,隔一段时间生成数据,自动插入数据。)
可以自定义一个waterMarkAssigner。也可以用BoundedOutOfOrdernessTimestampExtractor方法
.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor<Map<String, String>>(Time.minutes(10)) // 延迟时间
{
// 提取时间戳字段
@Override
public long extractTimestamp(Map<String, String> element) {
return Long.parseLong(element.get("ts"));
}
})
- AssignerWithPunctuatedWatermarks(自定义一个断点式生成watermark的Assigner)