本文已参与「新人创作礼」活动,一起开启掘金创作之路。
在事件时间语义(ProcessTime)下,利用商品的生产时间(数据的时间戳)了。我们可以这样思考:一般情况下,商品生产出来之后,就会立即传送到车上;所以商品到达车上的时间(系统时间)应该稍稍滞后于商品的生产时间(数据时间戳)。我们直接用商品生产时间来表示当前车上的时间了,这样就可以得到正确的统计结果了。
“水位线”(Watermark)就是类似于水流中用来做标志的记号,在 Flink 中,这种用来衡量事件时间(Event Time)进展的标记。
水位线可以在数据的时间戳基础上加一些延迟来保证不丢数据,这一点对于乱序流的正确处理非常重要(也就是可以增加延迟时间,允许一些延迟数据的到达)。
- 水位线主要的内容是一个时间戳,用来表示当前事件时间的进展
- 水位线是基于数据的时间戳生成的
- 水位线的时间戳必须单调递增,以确保任务的事件时间时钟一直向前推进
- 水位线可以通过设置延迟,来保证正确处理乱序数据
一个水位线 Watermark(t),表示在当前流中事件时间已经达到了时间戳 t, 这代表 t 之前的所有数据都到齐了,之后流中不会出现时间戳 t’ ≤ t 的数据
如何生成一个水位线
.assignTimestampsAndWatermarks() 方法需要传入一个 WatermarkStrategy 作为参数,这就是 所 谓 的 “ 水 位 线 生 成 策 略 ” 。 WatermarkStrategy 中 包 含 了 一 个 “ 时 间 戳 分 配器” TimestampAssigner 和一个“水位线生成器”WatermarkGenerator。
new BoundedOutOfOrdernessTimestampExtractor(),这个方法需要传入一个 maxOutOfOrderness 参数,表示“最大乱序程度”,它表示数据流中乱序数据时间戳的最大差值;如果我们能确定乱序程度,那么设置对应时间长度的延迟,就可以等到所有的乱序数据了。
有序流的水位线生成器本质上和乱序流是一样的,相当于延迟设为 0 的乱序流水位线生成器,两者完全等同:
//读取数据源,并行度为 1
val inputStream = env.readTextFile("/home/rjxy/zlp/Code/CodePro/Learn_Myself/Learn_Flink/src/main/resources/sensor.txt")
//乱序的Watermarks
inputStream.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[String](Time.seconds(10)) {
override def extractTimestamp(element: String): Long = {
val data = element.split(",")
//返回指定的字段作为水印字段,这里设置为10秒延迟
return data(1).toLong
}
//有序流的 Watermarks 生成器本质上和乱序流是一样的,相当于延迟设为 0 的乱序流水位线生成器,两者完全等同:
inputStream.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[String](Time.seconds(0)) {
override def extractTimestamp(element: String): Long = {
val data = element.split(",")
//返回指定的字段作为水印字段
return data(1).toLong
}
})
env.execute()
})
总结
水位线在事件时间的世界里面,承担了时钟的角色。也就是说在事件时间的流中,水位线是唯一的时间尺度。 水位线是一种特殊的事件,由程序员通过编程插入的数据流里面,然后跟随数据流向下游流动。
水位线的默认计算公式:水位线 = 观察到的最大事件时间 – 最大延迟时间 – 1 毫秒。