标题Flink 窗口函数介绍以及源码解析
流式计算是一种被设计用来处理无限数据集的数据处理引擎,而无限数据集是指一种不断增长的,本质上无限的数据集。而Window 窗口是一种切割无限数据集为有限块进行处理的手段。
Window 是无限数据流处理的核心,Window 将一个无限的stream 切分成有限个小的“bucket” 桶,我们可以基于这些桶做计算操作。
版本:
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java_2.11</artifactId>
<version>2.10</version>
Flink Windows 主要分2类
1.TimeWindow (根据实现原理又可以分3类)
-
TumblingWindow 滚动窗口
ds.timeWindow(Time.seconds(5) ) ,
一个参数,数据不会重复;统计每5秒窗口内的数据
-
SlidingWindow 滑动窗口
ds.timeWindow(Time.seconds(5) ,Time.Second(3)) ,
两个参数,窗口数据是可以重叠的,每隔3秒,统计最近5秒窗口内的数据; -
SessionWindow 会话窗口
window(ProcessingTimeSessionWindows.withGap(Time.seconds(3))),一个参数,如果连续3秒内,没有新的数据流入,那么上一个窗口关闭结束并计算输出该窗口内的数据;一旦有新数据流入,则开启新窗口
2. CountWindow (根据实现原理又可以分2类)
- TumblingWindow 滚动窗口
ds.countWindow(Time.Second(3)),
一个参数,当元素数量达到窗口大小,即触发窗口执行。 - SlidingWindow 滑动窗口
ds.keyby(0).countWindow(Time.Second(3),Time.Second(2)) ,
两个参数,每收到两个相同key的数据,则计算一次,每次计算的window 范围是3个元素。
Window源码部分
public WindowedStream<T, KEY, TimeWindow> timeWindow(Time size) {
*// 一个参数,走滚动窗口模式,TumblingxxxxTimeWindow 刚好也对的上*
// 进来首先判断Flink的时间语义,如果是ProcessingTime(执行基于时间操作算子的本地时间),那么走
//调用TumblingProcessingTimeWindows.of(size),否则后者
if (environment.getStreamTimeCharacteristic() == TimeCharacteristic.ProcessingTime) {
//ps:这里TimeWindow 底层调用的还是window 方法,和会话窗口方法调用是一样的,
//只不过滑动和滚动窗口对窗口函数做了封装而已
return window(TumblingProcessingTimeWindows.of(size)); //看A
} else {
return window(TumblingEventTimeWindows.of(size)); // 看E
}
}
//
public WindowedStream<T, KEY, TimeWindow> timeWindow(Time size, Time slide) {
if (environment.getStreamTimeCharacteristic() == TimeCharacteristic.ProcessingTime) {
return window(SlidingProcessingTimeWindows.of(size, slide)); //看F
} else {
return window(SlidingEventTimeWindows.of(size, slide));
}
}
A
这里走processingTime 窗口TumblingProcessingTimeWindows 类对象的of 方法,
首先将第二个参数置零,同时窗口长度转为毫秒单位
public static TumblingProcessingTimeWindows of(Time size) {
return new TumblingProcessingTimeWindows(size.toMilliseconds(), 0); 看B
}
B 在 TumblingProcessingTimeWindows.of(size) 和TumblingEventTimeWindows.of(size)的of 方法中,
都赋予offset 为0;offset 是时区差(用到的时候,会转为ms)
if (Math.abs(offset) >= size) {
throw new IllegalArgumentException("TumblingProcessingTimeWindows parameters must satisfy abs(offset) < size");
}
TumblingProcessingTimeWindows 类中,下面这个方法是真正分配窗口长度的代码位置。
public Collection<TimeWindow> assignWindows(Object element, long timestamp, WindowAssignerContext context) {
//上下文获取当前的processingTime
final long now = context.getCurrentProcessingTime();
//计算窗口的开始位置,看C
long start = TimeWindow.getWindowStartWithOffset(now, offset, size);
//这里是获取窗口的集合,重点我们看D
return Collections.singletonList(new TimeWindow(start, start + size));
}
C
public static long getWindowStartWithOffset(long timestamp, long offset, long windowSize) {
//这里获取窗口 的开始位置,实质上是根据窗口长度对ProcessingTime 向下取整
return timestamp - (timestamp - offset + windowSize) % windowSize;
}
D
//TimeWindow这个类中有maxTimestamp 这个方法,类中processingTime 的最大值为end - 1 ,这个说明了,窗口本质上左闭右开的,end 实际上要小1ms
public class TimeWindow extends Window {
@Override
public long maxTimestamp() {
return end - 1;
}
}
E
//ProcesssingTime 方法和eventTime区别在于,前者通过上下文获取操作算子执行任务的时间作为窗口
//计算时间,eventTime 则是根据事件指定时间字段的时间作为窗口计算时间。两者都调用了同样的
//方法,见C
@Override
public Collection<TimeWindow> assignWindows(Object element, long timestamp, WindowAssignerContext context) {
if (timestamp > Long.MIN_VALUE) {
// final long now = context.getCurrentProcessingTime(); 这个是区别核心,provessing是使用这个now
//作为第一个参数传到下面方法获取start, 而event是把时间制定时间作为参数计算获取start
long start = TimeWindow.getWindowStartWithOffset(timestamp, offset, size);
return Collections.singletonList(new TimeWindow(start, start + size));
} else {
throw new RuntimeException("Record has Long.MIN_VALUE timestamp (= no timestamp marker). " +
"Is the time characteristic set to 'ProcessingTime', or did you forget to call " +
"'DataStream.assignTimestampsAndWatermarks(...)'?");
}
}
F 其实processingTime 中of 方法和eventTime 中of 方法都是一样的,赋值第三个参数offset为0,同时将
窗口长度和滚动长度作为构造器参数传递
public static SlidingProcessingTimeWindows of(Time size, Time slide) {
return new SlidingProcessingTimeWindows(size.toMilliseconds(), slide.toMilliseconds(), 0);
}
这个是slidingEvent划分窗口的细节
这个是slidingProcessing划分窗口的细节,两者的区别还是在于时间语义的不一样,一个是系统的,一个是事件的。
未完待续。。。