大家好,我是雷恩Layne,这是《深入浅出flink》系列的第十一篇文章,希望能对您有所收获O(∩_∩)O
文章目录
一、Evictor扮演的角色
当一个元素进入stream中之后,一般要经历Window(开窗)、Trigger(触发器)、Evitor(移除器)、Windowfunction(窗口计算操作),具体过程如下:
- Window中的WindowAssigner(窗口分配器)定义了数据应该被分配到哪个窗口中,每一个 WindowAssigner都会有一个默认的Trigger,如果用户在代码中指定了窗口的trigger,默认的 trigger 将会被覆盖
- Trigger上会有定时器,用来决定一个窗口何时能够被计算或清除。Trigger的返回结果可以是 continue(不做任何操作),fire(处理窗口数据),purge(移除窗口和窗口中的数据),或者 fire + purge。一个Trigger的调用结果只是fire的话,那么会计算窗口并保留窗口原样,也就是说窗口中的数据仍然保留不变,等待下次Trigger fire的时候再次执行计算。一个窗口可以被重复计算多次知道它被 purge 了。在purge之前,窗口会一直占用着内存。
- 当Trigger fire了,窗口中的元素集合就会交给Evictor(如果指定了的话)。Evictor 主要用来遍历窗口中的元素列表,并决定最先进入窗口的多少个元素需要被移除。剩余的元素会交给用户指定的函数进行窗口的计算。如果没有 Evictor 的话,窗口中的所有元素会一起交给Windowfunction进行计算。
- Windowfunction收到了窗口的元素(可能经过了 Evictor 的过滤),并计算出窗口的结果值,并发送给下游。窗口计算操作有很多,比如预定义的sum(),min(),max(),还有 ReduceFunction,FoldFunction,Windowfunction。WindowFunction 是最通用的计算函数,其他的预定义的函数基本都是基于该函数实现的。
现在,大致了解了Evitor(移除器)扮演的角色,让我们继续往下看!
二、为何使用Evictor
假如有这么一个场景:stream每次进入一个元素(其CountTrigger.maxCount
设置为1)的时候获取最近2小时内的数据。这种情况下可以使用Flink提供的EventTimeSessionWindows
窗口分配器,此窗口分配器中接受一个gap参数。多个元素依次到来,如果这些元素之间的时间间隔均不大于gap,它们会被合并至同一个window中。如果两个元素的时间间隔大于gap,则之前的window会被截断,后面的元素会进入一个新的window中。
示例代码如下:
val stream = ...
stream.keyBy(0)
.window(EventTimeSessionWindows.withGap(Time.hours(2)))
.trigger(CountTrigger.of(1))
.evictor(TimeEvictor.of(Time.hours(2)))
当EventTimeSessionWindows
合并多个元素至同一个window中时,合并之后的window所含数据很可能存在2小时之前的数据。在计算前排除他们我们需要evictor来帮忙。所以,在示例代码中用TimeEvictor将2小时之前的老数据清理出去。
另外,代码中我们设置CountTrigger为1,所以每到来一个元素触发一次窗口计算,然后把数据发给evictor进行清理。
移除器evitor可以窗口触发计算前或触发计算后,定义移除某些数据的逻辑。Evictor接口定义如下:
public interface Evictor<T, W extends Window>