Flink中watermark为什么选择最小一条(源码分析)
昨天在社区群看到有人问,为什么水印取最小的一条?这里分享一下自己的理解
首先水印一般是设置为:(事件时间 - 指定的值) 这里的作用是解决迟到数据的问题,从源码来看一下它如何解决的
先来看下windowOperator.java接收到数据以后做了什么
在processElement方法中,会遍历这条数据属于的所有窗口执行
将窗口window作为Context的namaspace,这个window后面会被设置成每个定时器的namespace
因为这里是事件时间窗口所以会默认注册一个事件时间trigger,这是默认trigger的onElement方法
如果窗口的右边界已经小于当前水印时间了,就直接触发窗口计算
当返回continue时,也就是说水印还没有达到,这条数据属于的窗口的右边界,也就是说窗口还没有到触发的时机
可以看到这里他把这个数据属于的窗口的右边界注册成为一个触发器(timer)
这个触发器初始化的时候触发器的时间等于窗口右边界,且设置了触发器的namespace = window
这个timer有什么用呢,来看一下窗口触发的逻辑
所有的上游数据会从这里接收,在StreamInputProcessor.java的processInput()方法中有这样一段逻辑,当接收到水印
里面又调用了
从名字就可以知道是取了一个最小的水印,具体更新最小水印时间逻辑如下
这里就是我们的问题了,为什么他选取了最小的一个水印?
看看这段代码的后面他又做了什么
handleWatermark中调用peocessWatermark()方法
然后会走到这里
这里先使用最小水印更新了 当前的水印!!!!!!
这里会判断定时器时间是否小于当前最小水印时间(是触发 定时器的条件!!!!!)
当定时器的时间小于当前的事件时间时触发,在onEventTime()方法中
这里看到当返回fire时,会调用emitWindowContents()这个方法里面就会调用我们真正用户的process()方法了,而那个windowState.get()则是拿到了一个窗口中的所有数据
而,是否触发窗口就看onEventTime()方法是否返回Fire,具体实现如下
time就是定时器的时间,这个window就是定时器的namespace,就是这个定时器创建时传入的窗口,这里就是这个定时器时间的窗口
判断定时器的时间,变量time(前面我们将数据属于的窗口的右边界作为定时器的时间)是否等于这个定时器的window窗口(初始化传入的namespace参数这里就是注册定时器的window)右边界的时间,来决定窗口是否触发
!!!那既然最小水印是触发定时器的条件,定时器到时会触发窗口,那我们为什么会选择最小的水印来作为触发条件呢?
看下面这张图
可以看到一个窗口可能会有接收到许多的上游,每一个上游的流都会带有事件时间,那我们哪知道选用哪个流的水印时间作为窗口触发的条件呢?
有个最简单的办法就是:如果我上游每个流中取最小的水印,那就证明其他的水印时间肯定是大于最小的这个,我最小的一条流都达到了窗口的触发时间,那其他来自上游的流肯定都已经超过这个触发时间了,那我就可以触发这个窗口了