要想说清楚Flink水位线(WaterMark),前提需要弄清楚几个概念。
第一个是时间概念:
在Flink中有三个时间概念,分别是事件时间,采集时间,和系统时间。
事件时间:在客观世界中产生的时间,比如用户点击网页产生了一条时间日志,这个时间就是事件时间。
采集时间:我们用Flink采集日志到达Flink的时间为采集时间。
系统时间:这个时间使我们在写逻辑代码时会调用的时间,比如在程序里面写 System.currentTimeMillis() 命令
当有了时间概念,那我们真正关心的业务时间是哪个呢? 老板不会关心Flink什么时候采集的,更不会关心你什么时候在当前系统调用的时间,所以答案肯定是日志发生的时间,也就是用户什么时候点击的。
那我们怎么通过日志的事件时间对事件进行处理呢?
如下图所示:
当存在网络延时的情况下,本来进入窗口 A 的 2号数据在0-5秒内没有进入窗口A,但是它仍然在采集后出现在A窗口里面,这是为什么?
因为Flink将每条日志的客观产生的时间纪录下来,2号数据就是0-5秒产生的,Flink就会把它放在A窗口。只不过它是在5-10秒的时候被放进去的。那这样的话,我们应该什么时候去关闭窗口呢。
因为有种假设,如果有一个属于这个窗口的数据一年后才过来。那我岂不是要一年才能关窗。所以Flink为了解决这个问题设置了一个过期时间。设置方式如下:
其中10秒为过期时间,element._2为我们从日志中读取中的客观产生的时间戳。
虽然有了过期时间,但是Flink并不会只在过期时间之后闭窗。
实际上,Flink闭窗取决于水位线和过期时间的共同作用,那什么是水位线呢?
水位线指的是Flink内部会在每200ms(默认值,可以设置)生成一个时间戳,它的大小等于当前数据集合里面最大的客观时间减去延时时间。
Flink为什么这么设置呢,我们直接把0-5窗口的数据在延时10秒后关闭不就可以了么。答案是不可以
因为我们这个15秒是系统时间延时的10秒,但是我们要的是真正的客观的时间延时10秒。
这么说可能不太好理解,举个夸张的例子,客观世界中在0-15内(其中5秒的窗口,加上10秒的延时)内产生了十条数据,之后十年就不会再产生数据了,那Flink应不应该关窗?
实际上不应该关窗,因为计算机在不知道下一条日志客观时间的时候,他是不知道此时窗口进行到哪里了。只有在出现客观时间大于十五秒的时候,之前0-5秒的窗口才会关闭。
因此我们为了记录客观时间引入了水位线的概念。用这个数字来记录客观世界的最大时间。
我们只需要比较:水位线是否大于窗口+延时时间,来判断是否该关窗。
当水位线 > 窗口 ------>关窗
当水位线 < 窗口 ------>不关窗