一些基本概念介绍:

Event Time

事件时间是每个事件在其生产设备上发生的时间

Ingestion Time

摄取时间是数据进入Flink的时间

Processing Time

处理时间是是指正在执行相应算子操作的机器的系统时间,默认的时间属性就是Processing Time

[7]在Flink的流式处理中,绝大部分的业务都会使用EventTime,一般只在EventTime无法使用时,才会被迫使用ProcessingTime或者IngestionTime。默认情况下,Flink框架中处理的时间语义为ProcessingTime,如果要使用EventTime,那么需要引入EventTime的时间属性,引入方式如下所示:

import org.apache.flink.streaming.api.TimeCharacteristic

val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)

上面的这个只是为了表示当前的环境中有EventTIme属性,讲大白话就是输入的数据源中会有时间戳,不用想太多。

--------------------------------------------和时间相关的一些概念--------------------------------------------------------------------

假设nc -lk传入数据,intelliji处理数据,那么其实流处理的大概有这么几个时间相关的数据。

①终端输入数据的时间

②该数据中包含的时间

③intellij处理数据的时间

④window中的窗口宽度,slidingWindow还包括滑动时间宽度

⑤水位线时间, 这个是从上一条数据中得到的时间戳,另外还有水位线延迟时间(用来对付迟到的数据)

首先,①≠②,整个实验不关心①的时间,所以①我们不理会。

③我们一般也不关心(注意指的是一般情况下不关心,因为还是以数据源头中的时间戳中的Event Time为主)

④和⑤是我们关心范畴

所以主要是②④⑤

---------------------------------------------------------WaterMark和各种参数之间的关系--------------------------------------------------------------

什么是WaterMark呢,其实翻译过来就是水位线。

数据流好比水流(数据)从瀑布顶端倾泻而下,

而瀑布下方的水位线(数据生成的时间,Event Time)随着瀑布的流下而不断上升。

------------------------------------------------------------------------------------------------------------------------------------------------

Watermarks是基于已经收集的消息来估算是否还有消息未到达,本质上是一个时间戳。时间戳反映的是事件发生的时间,而不是事件处理的时间

所谓的事件(Event),讲人话其实就是一条数据。

1.设置StreamTime Characteristic为Event Time,即设置流式时间窗口

2.创建的DataStreamSource调用assignTimestampsAndWatermarks中设置WaterMarks种类(二选一):

①AssignerWithPeriodicWatermarks(周期性(一定时间间隔或者达到一定的记录条数)生成水印)

②AssignerWithPunctuatedWatermarks(数据流中每一个递增的 EventTime 都会产生一个 Watermark)

 

数据示例:

实验类型(代码)

nc -lk 3456输入

实验结果

实验解析

窗口的生命周期

 

tests
tests
tests

测试::7> (tests,3)

这个实验必须输入三个相同的字符串后,程序才会有反应

滚动窗口

later,6565

windows:>>>:7> window:[1599362540000-1599362545000]:{ (later,6565,1) }

window后面的是实际处理时间。

later后面手动输入的时间戳被忽视

滑动窗口

later,32234

windows:>>>:7> window:[1599364140000-1599364145000]:{ (later,32234,1) }

windows:>>>:7> window:[1599364143000-1599364148000]:{ (later,32234,1) }

一条数据出现在两个窗口中

会话窗口

test,12312

test,235233

 

windows:>>>:5> window:[1599366609562-1599366622057]:{ (test,12312,1),(test,235233,1) }

processtime间隔时间超过10s,就会输出上一个窗口(含10s所有输入内容)

这里的例如1599366609562单位是ms

我们根据业务的需求还判断使用哪个时间类型,一般来说使用Event Time更多,比如:在统计最近5分钟的订单总金额时,我们需要的是真实的订单时间,而不是进入flink的时间或者是处理时间。

另外,生产环境中大多数情况下数据源头都会带有时间戳,时间戳经过flink处理提取得到Event Time。

上述三种窗口的事件事件写法与处理时间写法的对照(不包含窗口偏移设置)

 

事件时间(Event Time)

处理时间

TumblingWindow

.window(TumblingEventTimeWindows.of(Time.seconds(5)))

.window(TumblingProcessingTimeWindows.of(Time.seconds(5)))

SlidingWindow

.window(SlidingEventTimeWindows.of(Time.seconds(10), Time.seconds(5)))

.window(SlidingProcessingTimeWindows.of(Time.seconds(10), Time.seconds(5)))

SessionWindow

.window(EventTimeSessionWindows.withGap(Time.minutes(10)))

.window(ProcessingTimeSessionWindows.withGap(Time.minutes(10)))

上述三种窗口的事件事件写法与处理时间写法的对照(包含窗口偏移设置)

 

事件时间(Event Time)

处理时间

TumblingWindow

.window(TumblingEventTimeWindows.of(Time.seconds(5),Time.seconds(3)))

.window(TumblingProcessingTimeWindows.of(Time.seconds(5),Time.seconds(3)))

SlidingWindow

.window(SlidingEventTimeWindows.of(Time.seconds(10), Time.seconds(5),Time.seconds(3)))

.window(SlidingProcessingTimeWindows.of(Time.seconds(10), Time.seconds(5),Time.seconds(3)))

SessionWindow

.window(EventTimeSessionWindows.withGap(Time.minutes(10),Time.seconds(3)))

.window(ProcessingTimeSessionWindows.withGap(Time.minutes(10),Time.seconds(3)))

系统时间可以理解为是intellij处理数据的时间。

而EventTimeWindows那就是让窗口以事件中的时间戳(Event TIme)作为时间标尺了。

-----------------------------------------------下面是两个看似矛盾的说法--------------------------------------------------------------------------

[1]WaterMark为上一条数据的Event Time,并非当前的WaterMark

[2]WaterMark为当前数据的Event Time(根据实验输出来看)

为什么会有这么两种截然不同的说法呢?

这是因为:

[1]的WaterMark是在getCurrentWatermark()函数中输出的。

[2]的WaterMark是在extractTimestamp()函数中输出的。

所以其实不矛盾

-----------------------------------------------------对于包含WaterMark的程序的触发条件--------------------------------------
①并行度大于1的时候,需要各个节点都满足②③。
 在多并行度的情况下,Watermark 会有一个对齐机制,这个对齐机制会取所有 Channel中最小的 Watermark。
②window条件要满足(例如每5条数据触发)、
③算子和定时器相关,需要waterMark高于定时器设置的时间。例如ctx.timerService().registerEventTimeTimer(value.getBizTime());

---------------------------------------------------------------------------------------------------------------------------------------

接收到水位线以前的的消息是不可避免的,这就是所谓的迟到事件。实际上迟到事件是乱序事件的特例,和一般乱序事件不同的是它们的乱序程度超出了水位线的预计,导致窗口在它们到达之前已经关闭。[8]

迟到事件出现时窗口已经关闭并产出了计算结果,因此处理的方法有3种:[8]

  • 重新激活已经关闭的窗口并重新计算以修正结果(Side Output)。
  • 将迟到事件收集起来另外处理(Allowed Lateness)。
  • 将迟到事件视为错误消息并丢弃。

Flink 默认的处理方式是第3种直接丢弃,其他两种方式分别使用Side OutputAllowed Lateness。[8]

接收到水位线以前的的消息是不可避免的,这就是所谓的迟到事件。实际上迟到事件是乱序事件的特例,和一般乱序事件不同的是它们的乱序程度超出了水位线的预计,导致窗口在它们到达之前已经关闭。[8]

上面都是一些概念,实验方面的记录在[9]