Flink 触发机制的关键模型
前面的文章,提到Watermark(以下用wm代替)的时候,就说过wm为触发,提供参照物的作用。今天就看用源码看看。
这里有两个情况,先展开讨论下,
- 第一是,没有窗口,也就意味着,每条数据,都需要根据wm来判断是否触发。
- 第二是 ,有窗口,意味着,每个窗口,都需要根据wm来判断是否触发。
虽然是两种情况,但是具有共性特征,窗口也有时间,用窗口的endTime代表窗口触发的时间,就和数据触发过程无异了。
聊到了这,就有两个问题需要解答,数据或窗口的时间怎么保存,怎么触发。
以窗口为例,
一个窗口可能对应n条数据,每次处理同一个窗口n条数据时,都会产生相同的窗口数据,如果都保存显然不靠谱,优化去重是需要的。如果去重后,数据以什么结构来保存呢?数据的来源不是严格有序的,可以采用堆结构,或者RocksDB存储。
数据存好了,怎么触发呢?代码中在更新获得的wm时,就会判断堆结构的peek() 是否小于等于wm,poll()数据,进行逻辑操作,直到堆结构为空或peek()>wm。
现在来看看源码,先看下触发部分
// 这是 InternalTimerServiceImpl 类 的advanceWatermark 方法,传递的参数就是wm。
public void advanceWatermark(long time) throws Exception {
currentWatermark = time;
InternalTimer<K, N> timer;
// 从eventTimeTimersQueue中获取堆顶元素
while ((timer = eventTimeTimersQueue.peek()) != null && timer.getTimestamp() <= time) {
eventTimeTimersQueue.poll();
keyContext.setCurrentKey(timer.getKey());
triggerTarget.onEventTime(timer);
}
}
再来看数据存储结构
//这是EventTimeTrigger类,处理每条数据的方法
public TriggerResult onElement(
Object element, long timestamp, TimeWindow window, TriggerContext ctx)
throws Exception {
//数据对应窗口的最大时间,如果<=wm,直接触发
if (window.maxTimestamp() <= ctx.getCurrentWatermark()) {
// if the watermark is already past t