Window Lifecycle (生命周期)
窗口生命周期:简而言之,什么时候创建和什么时候时候关闭,整个生命周期就是窗口创建到关闭,期间还涉及窗口怎么划分和窗口什么时候触发执行
窗口划分
窗口的形成即是根据窗口起始时间和窗口长度即可形成,即确定起始时间和窗口长度即可获得窗口结束时间。
- 开始时间
开始时间=timestamp-(timestamp - offset + windowSize) % windowSize
。对窗口长度
取整数倍(以1970年1月1日0点为基准,因为时间戳是基于1970年1月1日0点
) 所以,开始时间并不是以第一条数据 作为窗口的起始点。注:开始时间是包含。
个人理解 timestamp 指的是事件时间戳,基于 1970年1月1日0点为准也可以理解来自数据中的时间戳 offset 默认东八时区0点 也是基于 `1970年1月1日0点为准
- 结束时间
结束时间 =开始时间+窗口长度
,窗口的长度一定会指定。注:结束时间是不包含
示例:
事件时间=1s,窗口长度为10s,那么窗口就是[0-10) 1- (1 - 0 + 10) % 10 => 1-1 => 0
事件时间= 1648806746000 ,窗口长度为5秒,那么窗口就是(1648806745000-1648806750000) 4600 - (4600 - 0 + 5000) % 5000 => 4600 - 1000 => 4500
注:
在开始时间的案例可以解答前面的事件时间的代码运行结果为什么是整数 例如:(输入的时间戳是 1648806746000 窗口大小是 5s 但是在运行结果中范围是 1648806745000-1648806750000),可以去看一下前面的 事件时间的滑动窗口和滚动窗口里面的案例 都是这样,运行查看一下
窗口触发
为什么窗口会选择左闭右开呢?
- 左闭右开能够包含所有数据,既不会漏掉左边数据,还能无限接收右边数据。
- 窗口的右边为最大时间戳,最大时间戳maxTimestamp =
end - 1ms
示例:
[0,10) => 该窗口的最大时间戳maxTimestamp = 10s -1ms = 9999ms 。所以10s这条数据,不属于该窗口,所以是开区间。
窗口触发
窗口确定了,但是窗口什么时候开始执行呢? 通常是2个条件(基于EventTimeWindow
):
- watermark >= 窗口的最大时间戳
- 窗口中有数据
基于事件时间窗口,上述2个条件同时达到即可触发窗口执行。
示例:
window.maxTimestamp() <= ctx.getCurrentWatermark() ==> watermark >= 窗口的最大时间戳
=> 比如,[0,10)的窗口,当 watermark >= 10s - 1ms = 9999ms时触发
=> 比如,窗口需等待3s触发,当watermark = 事件时间 - 等待3s - 1ms => 事件时间 = 13s时触发
窗口创建
创建窗口:当属于该窗口的第一个元素到达时,该窗口就会被创建。创建条件:
- 窗口的第一条数据来的时候,new出来
- 窗口的每条数据来的时候,都会new,但是集合是singleton类型,不会重复存放 => 也就是说,只会有一个窗口对象。
示例:
return Collections.singletonList(new TimeWindow(start, start + size));
=> 每来一条数据,都会 new一个窗口对象
=> 将窗口对象,放入一个 SingletonList,是单例的,所以不会重复,只是一个引用
=> 窗口结束时间 end = 计算出来的 start + 窗口长度
窗口关闭
当时间(事件或处理时间
)超过其他结束时间戳加上用户指定的允许延迟时(窗口结束时间+ 等待时间
),该窗口将被完全删除。关闭条件:
- 窗口关闭时间 = 窗口最大时间戳+allowedLateness
延迟时间
。如果allowedLateness = 0(不设置),那么触发和关闭的时间一样
注意:
[0,10) => 该窗口的最大时间戳maxTimestamp = 10s -1ms = 9999ms
如未设置allowedLateness,窗口关闭时间 = 9999ms + 0 = 9999ms
建议看完这篇文章之后去看前面的 Time和Window 和 事件和处理时间的滑动 滚动窗口 等等 能更了解窗口的生命的周期