Flink自定义Assigning Timestamps和Watermarks

为使Flink的event time正常工作,需为流中元素分配事件时间戳,同时生成水印告知系统事件时间进度。文章介绍了几种自定义事件时间戳的方法,如在数据流源中定义、使用DataStream API指定等,还说明了窗口触发条件及水印含义。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为了让event time工作,Flink需要知道事件的时间戳,这意味着流中的每个元素都需要分配其事件时间戳。这个通常是通过抽取或者访问事件中某些字段的时间戳来获取的。时间戳的分配伴随着水印的生成,告诉系统事件时间中的进度。下面介绍几种自定义事件时间戳方法
1.在数据流源中定义
可以看Flink静态Session Windows这边文章里面有
2.使用DataStream API中的assignAscendingTimestamps来指定时间戳。其中系统默认用此时间戳创建Watermark。注意::数据源任务中的时间戳是递增的,这是很必要的。

  def main(args: Array[String]) {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
    val input=env.fromCollection(List(("a",1L),("b",1L),("b",5L),("b",5L)))
    val timeWindow=input.assignAscendingTimestamps(t=>t._2)
    val result=timeWindow.keyBy(0).timeWindow(Time.milliseconds(4)).sum("_2")
    result.print()
    env.execute()
  }

结果:
result
3.实现BoundedOutOfOrdernessTimestampExtractor类

val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
    env.getConfig.setAutoWatermarkInterval(90000)
    val input=env.fromCollection(List(("b",1L),("b",2L),("b",3L),("b",4L),("b",5L),("b",6L),("b",7L),("b",8L),("b",9L)))
    val timeWindow=input.assignTimestampsAndWatermarks(
      new BoundedOutOfOrdernessTimestampExtractor[(String, Long)](Time.milliseconds(1)) {
      override def extractTimestamp(element: (String, Long)): Long = element._2
    })
    val result=timeWindow.keyBy(0).timeWindow(Time.milliseconds(4)).sum("_2")
    result.print()
    env.execute()

结果:
result2
注意:此类有一个参数Time.milliseconds(1),代表最长的时延1ms。可以查看源码
result34实现AssignerWithPeriodicWatermarks接口


  def main(args: Array[String]) {
    val params = ParameterTool.fromArgs(args)
    val senv = StreamExecutionEnvironment.getExecutionEnvironment
      senv.getConfig.setAutoWatermarkInterval(900000)
    senv.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
    val text = senv.socketTextStream("localhost", 9999)
      .assignTimestampsAndWatermarks(new TimestampExtractor)
    val counts = text.map {(m: String) => (m.split(",")(0), 1) }
      .keyBy(0)
      .timeWindow(Time.milliseconds(10))
      .sum(1)
    counts.print
    senv.execute("EventTime processing example")
  }
  class TimestampExtractor extends AssignerWithPeriodicWatermarks[String] with Serializable {
    private var currentMaxTimestamp = 0L
    private val maxOutOfOrderness = 3l
    override def extractTimestamp(e: String, prevElementTimestamp: Long) = {
      val timestamp=e.split(",")(1).toLong
     // println( e.split(",")(1).toLong)
      currentMaxTimestamp = Math.max(prevElementTimestamp,timestamp)
      e.split(",")(1).toLong

    }
    override def getCurrentWatermark(): Watermark = {
      println(currentMaxTimestamp-maxOutOfOrderness)
      new Watermark(currentMaxTimestamp-maxOutOfOrderness)
    }
  }

输入:
在这里插入图片描述
结果:
在这里插入图片描述
在这里插入图片描述
注意:1.窗口触发需要要满足两个条件:1.watermark>=window_end_time,2,此窗口内有数据。
2.同时也说明watermark对window的分段之间没有关系,比如输入(a,13),(a,12),(a,16)都在10ms~20ms窗口内
5.实现AssignerWithPunctuatedWatermarks接口

def main(args: Array[String]) {

    // Checking input parameters
    val params = ParameterTool.fromArgs(args)

    // set up the execution environment
    val senv = StreamExecutionEnvironment.getExecutionEnvironment
      senv.getConfig.setAutoWatermarkInterval(900000)
    senv.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
    val text = senv.socketTextStream("localhost", 9999)
      .assignTimestampsAndWatermarks(new TimestampExtractor)
    val counts = text.map {(m: String) => (m.split(",")(0), 1) }
      .keyBy(0)
      .timeWindow(Time.milliseconds(10))
      .sum(1)
    counts.print
    senv.execute("EventTime processing example")
  }
  class TimestampExtractor extends AssignerWithPunctuatedWatermarks[String] with Serializable {

    override def checkAndGetNextWatermark(lastElement: String, extractedTimestamp: Long): Watermark = {
      if(lastElement.split(",")(1).toLong%2==0)
        {
          println(extractedTimestamp)
          new Watermark(extractedTimestamp)
        }
      else null
    }

    override def extractTimestamp(element: String, previousElementTimestamp: Long): Long ={
      element.split(",")(1).toLong
    }
  }

结果:
在这里插入图片描述总结:其中2~4是固定时延间隔指定timestamps和watermark,5是根据事件的特殊条件。
从中可以看出watermark的含义是在固定时延间隔乱序,整体是有序的。

### 回答1: Flink自定义SinkSource是指用户可以根据自己的需求,编写自己的数据源数据输出方式。Flink提供了一些内置的SinkSource,但是有时候用户需要根据自己的业务需求,自定义数据源数据输出方式。 自定义SinkSource需要实现Flink提供的接口,例如实现SinkFunction接口来自定义数据输出方式,实现SourceFunction接口来自定义数据源。用户可以根据自己的需求,实现这些接口,然后将自定义的SinkSource应用到Flink程序中。 自定义SinkSource可以帮助用户更好地满足自己的业务需求,提高数据处理的效率准确性。 ### 回答2: Flink自定义SinkSource方便开发人员根据特定业务需求定制化的数据输入输出。这也是Flink作为DataStream处理引擎的一个强大特性。 自定义Sink的主要作用是将Flink处理的数据流输出到外部存储或处理系统中,如Kafka、Hadoop、Elasticsearch、MySQL等。通过自定义Sink,我们可以满足不同业务场景下,数据输出的不同需求。 自定义Sink的实现需要继承Flink提供的`RichSinkFunction`或者`SinkFunction`抽象类,并实现其抽象方法。`RichSinkFunction`中提供了一些状态管理的方法,如`open`、`close`等,我们可以在这些方法中添加额外的代码逻辑。自定义的SinkFunction可以重写invoke方法,将不需要状态管理的代码集中在此方法中。 自定义Source的主要作用是将外部数据源中的数据读取并发送给Flink的DataStream处理模块。自定义Source可以读取各种类型的数据源,如Kafka、文件、Socket等。 自定义Source的实现需要继承Flink提供的`RichParallelSourceFunction`或者`SourceFunction`抽象类,并实现其抽象方法。`RichParallelSourceFunction`中支持在并行算子中运行,因此对于大规模数据的处理尤为适合。 在自定义Source中,需要实现一个`run`方法一个`cancel`方法。`run`方法中是数据源处理逻辑的主要实现,`cancel`方法用于停止数据源的读取。我们还可以通过Flink提供的Checkpoint机制来管理数据源。 总之,自定义SinkSource是Flink处理数据流的重要特性,使得开发人员可以根据业务需求灵活定制化的输入输出逻辑。 ### 回答3: Flink是一个开源流式处理框架,它提供了丰富的内置SinkSource,同时也支持用户自定义的SinkSource,以便满足不同的业务需求。 自定义Sink可以用于将流式数据写入外部系统中,比如数据库、消息队列文件系统等。Flink提供了一个简单的接口SinkFunction,通过实现该接口可以快速开发自己的Sink。 SinkFunction接口定义了一个抽象方法invoke(),该方法是在每个输入元素处理完成时被调用。开发者需要编写自己的业务逻辑,在invoke()中实现将数据写入目标系统的逻辑。 自定义Source可以用于从外部系统读取数据,并将其逐个交付给Flink程序进行处理。同样地,Flink也提供了一个简单的接口SourceFunction,通过实现该接口可以快速开发自己的Source。 SourceFunction接口定义了两个抽象方法:run()cancel()。run()方法是在源自生命周期内调用的,它是源自执行主逻辑的地方。cancel()方法是用于清理资源的。开发者需要在run()方法中编写从外部系统读取数据的逻辑,并且能够异步地产生数据,最后将数据通过SourceContext将数据一条一条源源不断地输出。 自定义SinkSource是Flink框架中非常常用的一个扩展方式,它可以满足用户自定义的需求,在具体的业务场景中,能够灵活的使用自定义SinkSource对数据的处理进行个性化的定制化。同时,自定义SinkSource的开发也相对简单,可以通过实现简单的接口,快速完成自定义SinkSource的开发。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值