一、概述
在日常生活场景中,我们经常需要获取某一个事件段的数据,如上文所说,希望统计在10点-11点事件段数据的具体情况,那么如何进行处理呢?
如有看过前文(Flink学习笔记(七):flink聚合函数)的小伙伴们可能会记得有一个countWindow,这就是flink给我们提供的算子(窗口,window),使用窗口方法我们可以将数据流分成一个个窗口,然后对窗口内的数据进行处理。
我们先看下flink官网对窗口的描述
windows是处理无限流的核心。 Windows将流分成有限大小的“存储桶”,我们可以在其上应用计算。 本文档重点介绍如何在Flink中执行窗口化,以及程序员如何从其提供的功能中获得最大收益。
窗口式Flink程序的一般结构如下所示。 第一个片段是指键控流,而第二个片段是指非键控流。 可以看到,唯一的区别是对键控流的keyBy(…)调用和对非键控流的window(…)变为windowAll(…)。
Keyed Windows
stream
.keyBy(...) <- keyed versus non-keyed windows
.window(...) <- required: "assigner"
[.trigger(...)] <- optional: "trigger" (else default trigger)
[.evictor(...)] <- optional: "evictor" (else no evictor)
[.allowedLateness(...)] <- optional: "lateness" (else zero)
[.sideOutputLateData(...)] <- optional: "output tag" (else no side output for late data)
.reduce/aggregate/fold/apply() <- required: "function"
[.getSideOutput(...)] <- optional: "output tag"
Non-Keyed Windows
stream
.windowAll(...) <- required: "assigner"
[.trigger(...)] <- optional: "trigger" (else default trigger)
[.evictor(...)] <- optional: "evictor" (else no evictor)
[.allowedLateness(...)] <- optional: "lateness" (else zero)
[.sideOutputLateData(...)] <- optional: "output tag" (else no side output for late data)
.reduce/aggregate/fold/apply() <- required: "function"
[.getSideOutput(...)] <- optional: "output tag"
在上面,方括号([…])中的命令是可选的。 这表明Flink允许您以多种不同方式自定义窗口逻辑,从而使其最适合您的需求。
名词解释:
keyBy:按照一个Key进行分组
window:将数据流中的元素分配到相应的窗口中
windowAll:不分组,将数据流中的所有元素分配到相应的窗口中
trigger:指定触发器Trigger(可选)
evictor:指定清除器Evictor(可选)
allowedLateness:将窗口关闭时间再延迟一段时间
sideOutputLateData:所有过期延迟数据,指定窗口已经彻底关闭了,就会把数据放到侧输出流
.reduce/aggregate/fold/apply(): 窗口处理函数Window Function
二、分类
flink为我们提供了 Window Assigner(窗口分配器)
窗口分配器定义了如何将元素分配给窗口。这是通过在window(…)(对于键控流)或windowAll()(对于非键控流)调用中指定您选择的WindowAssigner来完成的。
按照窗口的Assigner来分,窗口可以分为
Tumbling window,
sliding window,
Session Window,
global window,
custom window,
count window
窗口可以是时间驱动的(Time Window,例如:每30秒钟),也可以是数据驱动的(Count Window,例如:每一百个元素)。
一种经典的窗口分类可以分成:
翻滚窗口(Tumbling Window,无重叠)
滚动窗口(Sliding Window,有重叠),
和会话窗口(Session Window,活动间隙)。
1、时间驱动窗口
2、数据驱动窗口
3、会话驱动窗口
三、实现
我们看一下官方的实现
1、Tumbling windows
DataStream<T> input = ...;
// tumbling event-time windows
input
.keyBy(<key selector>)
.window(TumblingEventTimeWindows.of(Time.seconds(5)))
.<windowed transformation>(<window function>);
// tumbling processing-time windows
input
.keyBy(<key selector>)
.window(TumblingProcessingTimeWindows.of(Time.seconds(5)))
.<windowed transformation>(<window function>);
// daily tumbling event-time windows offset by -8 hours.
input
.keyBy(<key selector>)
.window(TumblingEventTimeWindows.of(Time.days(1), Time.hours(-8)))
.<windowed transformation>(<window function>);
2、Sliding windows
DataStream<T> input = ...;
// sliding event-time windows
input
.keyBy(<key selector>)
.window(SlidingEventTimeWindows.of(Time.seconds(10), Time.seconds(5)))
.<windowed transformation>(<window function>);
// sliding processing-time windows
input
.keyBy(<key selector>)
.window(SlidingProcessingTimeWindows.of(Time.seconds(10), Time.seconds(5)))
.<windowed transformation>(<window function>);
// sliding processing-time windows offset by -8 hours
input
.keyBy(<key selector>)
.window(SlidingProcessingTimeWindows.of(Time.hours(12), Time.hours(1), Time.hours(-8)))
.<windowed transformation>(<window function>);
3、Session windows
DataStream<T> input = ...;
// event-time session windows with static gap
input
.keyBy(<key selector>)
.window(EventTimeSessionWindows.withGap(Time.minutes(10)))
.<windowed transformation>(<window function>);
// event-time session windows with dynamic gap
input
.keyBy(<key selector>)
.window(EventTimeSessionWindows.withDynamicGap((element) -> {
// determine and return session gap
}))
.<windowed transformation>(<window function>);
// processing-time session windows with static gap
input
.keyBy(<key selector>)
.window(ProcessingTimeSessionWindows.withGap(Time.minutes(10)))
.<windowed transformation>(<window function>);
// processing-time session windows with dynamic gap
input
.keyBy(<key selector>)
.window(ProcessingTimeSessionWindows.withDynamicGap((element) -> {
// determine and return session gap
}))
.<windowed transformation>(<window function>);
4、Global Windows
DataStream<T> input = ...;
input
.keyBy(<key selector>)
.window(GlobalWindows.create())
.<windowed transformation>(<window function>);
相关唇口方法中可以使用Time.milliseconds(x),Time.seconds(x),Time.minutes(x) 等之一来指定时间间隔。
窗口分配器还采用可选的offset参数,该参数可用于更改窗口的对齐方式。 例如,在没有偏移的情况下,每小时滚动窗口与标注时间对齐,即您将获得诸如1:00:00.000-1:59:59.999、2:00:00.000-2:59:59.999之类的窗口。 如果要更改,可以提供一个偏移量。 例如,使用15分钟的偏移量,您将获得1:15:00.000-2:14:59.999、2:15:00.000-3:14:59.999等。
偏移量的重要用例是将窗口调整为时区 除了UTC-0。 例如,在中国,您将必须指定**Time.hours(-8)**的偏移量。
注意:窗口左闭右开