Flink 水位线(Watermark)(二)

190 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

事件时间语义(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 毫秒。