Stream API开发指南之EventTime &Watermark

Event Time

使用Event Time首先要设置Time Characteristic

env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime);

watermark

  • 生成水印的两大类
    • 直接在数据源端产生
    • 通过时间戳分配和water 生成器产生
  • 示例代码(数据源端产生)
override def run(ctx: SourceContext[MyType]): Unit = {
	while (/* condition */) {
		val next: MyType = getNext()
		ctx.collectWithTimestamp(next, next.eventTimestamp)

		if (next.hasWatermarkTime) {
			ctx.emitWatermark(new Watermark(next.getWatermarkTime))
		}
	}
}
  • 通过时间戳分配器和water生成器产生。总体上来说又分两种情况

    • AssignerWithperiodicWatermarks 周期性产生 (这也是实际中最常用的)

    • AssignerWithPunctuatedWatermarks 特殊事件产生,意味着根据接入的事件来触发水印

    • 类图关系如下

      img

  • 周期水印实现方式

    • 第一种,实现AssignerWithPeriodicWatermarks 接口并实现相关函数

      示例: 给了两种自定义实现接口的实例,两者的主要区别是可以看到后者watermark是一个固定值,而前者的水印不一定,准确来说,区别在于,后者假设的是一种固定延迟的情况,具体差别可以看下图

      class BoundedOutOfOrdernessGenerator extends AssignerWithPeriodicWatermarks[MyEvent] {
      
          val maxOutOfOrderness = 5000L // 3.5 seconds
      
          var currentMaxTimestamp: Long = _
      
          override def extractTimestamp(element: MyEvent, previousElementTimestamp: Long): Long = {
              val timestamp = element.getCreationTime()
              currentMaxTimestamp = max(timestamp, currentMaxTimestamp)
              timestamp
          }
      
          override def getCurrentWatermark(): Watermark = {
              // return the watermark as current highest timestamp minus the out-of-orderness bound
              new Watermark(currentMaxTimestamp - maxOutOfOrderness)
          }
      }
      
      /**
       * This generator generates watermarks that are lagging behind processing time by a fixed amount.
       * It assumes that elements arrive in Flink after a bounded delay.
       */
      class TimeLagWatermarkGenerator extends AssignerWithPeriodicWatermarks[MyEvent] {
      
          val maxTimeLag = 5000L // 5 seconds
      
          override def extractTimestamp(element: MyEvent, previousElementTimestamp: Long): Long = {
              element.getCreationTime
          }
      
          override def getCurrentWatermark(): Watermark = {
              // return the watermark as current time minus the maximum time lag
              new Watermark(System.currentTimeMillis() - maxTimeLag)
          }
      }
      

      img

      img

    • 第二种 使用BoundedOutOfOrdernessTimestampExtractor

      示例:

      应用场景:固定延迟,比如示例中传入的就是10s

          .assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[Int](Time.seconds(10)) {
              override def extractTimestamp(element: Int): Long = {
                element
              }
            })
      
    • 第三种 使用IngestionTimeExtractor

      可以看到,使用IngestionTimeExtractor甚至不需要做任何操作,调用即可,实际上是因为底层实现了,如图所示:

      在这里插入图片描述

      示例:

       .assignTimestampsAndWatermarks(new IngestionTimeExtractor[Int])
      
      
    • 第四种 使用AscendingTimestampExtractor

      示例:

      应用场景:当 kafka 作为数据源时,kafka 的每个 Partition 分区里面时间戳可能是升序或者乱序模式。通常情况,我们会多个 Partition 分区并行处理,我们可以为 kafka 配置水印。
      kafka 内部为每个 Partition 分区维护一个水印,并且在流进行 shuffle 时以2.3 并行流中的 Watermarks进行水印合并

      val kafkaSource = new FlinkKafkaConsumer09[MyType]("myTopic", schema, props)      
      kafkasource.assignTimestampsAndWatermarks(new AscendingTimestampExtractor[Int] {
              override def extractAscendingTimestamp(element: Int): Long = {
                element
              }
            })
      env.addSource(kafkasource)
      

      注意:注意:可以在每个事件上生成水印。但是,由于每个水印都会在下游引起一些计算,因此过多的水印会降低性能。

      贴一张图说明吧:

在这里插入图片描述

  • 第五种

    • 使用assignAscendingTimestamps 分配Timestamp,实际上

    • 示例

      val withTimestampsAndWatermarks = stream.assignAscendingTimestamps( _.getCreationTime )
      
      
    • 实际上底层使用的还是assignTimestampsAndWatermarks,可以看一下源码就能得出结论

      在这里插入图片描述

  • 特殊水印的实现

    • 从图上可以看出,特殊水印的实现方式有两种,即

      • 自定义实现
      • 使用AssignerWithPunctuatedWatermarks 类
    • 第一种方式

      class PunctuatedAssigner extends AssignerWithPunctuatedWatermarks[MyEvent] {
      
      	override def extractTimestamp(element: MyEvent, previousElementTimestamp: Long): Long = {
      		element.getCreationTime
      	}
      
      	override def checkAndGetNextWatermark(lastElement: MyEvent, extractedTimestamp: Long): Watermark = {
      		if (lastElement.hasWatermarkMarker()) new Watermark(extractedTimestamp) else null
      	}
      }
      
    • 第二种方式

            .assignTimestampsAndWatermarks(new AssignerWithPunctuatedWatermarks[Int] {
              override def checkAndGetNextWatermark(lastElement: Int, extractedTimestamp: Long): Watermark = {
                ???
              }
      
              override def extractTimestamp(element: Int, previousElementTimestamp: Long): Long = {
                ???
              }
            })
      

    参考文章:
    https://blog.csdn.net/xiaohulunb/article/details/103148516
    ​ https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/dev/event_timestamps_watermarks.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值