FLink底层引擎是一个流式引擎,支持流处理和批处理,而window是streaming到batch的桥梁。因为流处理过程中,数据是源源不断流进来的,需要对数据进行实时处理的话,可以通过来一个消息处理一个的方式,也可以通过把一段时间内的数据聚合之后,再一起处理的形式,此时需要定义一个窗口来收集过去那段时间内的数据再进行处理。
Flink 提出了三种时间的概念,分别是event time(事件时间:事件发生时的时间),ingestion time(摄取时间:事件进入流处理系统的时间),processing time(处理时间:消息被计算处理的时间)。
窗口类型
窗口可以是时间驱动的(Time Window,例如:每30秒钟),也可以是数据驱动的(Count Window,例如:每一百个元素)。一种经典的窗口分类为:
- Tumbling window (滚动窗口,无重叠)
- sliding window (滑动窗口,时间有重叠)
滑动窗口分配器将元素分配给固定长度的窗口。类似于滚动窗口分配器,窗口的大小由窗口大小参数配置。另外一个参数控制滑动窗口的启动频率。因此,如果频率小于窗口尺寸,滑动窗可以重叠。在这种情况下,元素被分配到多个窗口。
例如,使用大小为10分钟的窗口,滑过5分钟。如下图所示。
- session window(会话窗口,无重叠)
会话窗口通过活动会话分配组元素。与滚动窗口和滑动窗口相比,会话窗口不重叠,没有固定的开始和结束时间。相反,当会话窗口在一段时间内没有接收到元素时,即当发生不活动的间隙时关闭。会话窗口分配器配置有会话间隙,定义所需的不活动时间长度。当此时间段到期时,当前会话关闭,后续元素被分配到新的会话窗口。
还可以分别结合以时间驱动或者数据驱动,如:sliding time window,tumbling count window。
窗口常用的组件:
Window Assigner :决定某个元素被分配到哪个/哪些窗口中去。
Trigger : 触发器,进行窗口的处理或清除,每个窗口都会拥有一个的Trigger。
Evictor : “驱逐者”,类似filter作用。在Trigger触发之后,window被处理前,EVictor用来处理窗口中无用的元素。
窗口应用在join操作
由以上可以得知,若要对两条数据流进行join操作,则一定是基于window形式的,同样的还有和join操作类似的CoGroupedStreams。可以发现,Flink中joinStream会通过调用windowStream来实现。如图。
接下来,对join的一个实现类WindowJoin进行分析。基本思想为在一个时间窗内对两条数据结构为键值对数据流进行inner join操作。
重要参数配置:根据Flink的时间概念,时间属性时间选为ingestion time,并设置了窗口大小和数据传输速率。
函数调用
where():给两条数据流指定各自的keySelector,获取key的类型
equal()判断key是否相同
-
window():制作一个ID标识符,配置窗口中的
- 输入流DataStream、keySelector、key type等元数据
- 窗口组件window assigner、Trigger、EVictor
apply():配置join操作方法
最后通过execute()执行inner join操作
问题
join 窗口的双流数据都是被缓存在内存中的,也就是说如果某个key上的窗口数据太多就会导致 JVM OOM。双流join的难点也正是在这里。例如可以借鉴Flink在批处理join中的优化方案,也可以像HDFS对中间结果的操作那样,当数据超过阈值时能spill到硬盘。