【问题标题】:Flink WaterMark And Triggers - Late elements not discarded on event time?Flink WaterMark 和触发器 - 事件时间未丢弃的后期元素?
【发布时间】:2018-05-01 09:47:03
【问题描述】:

我对 Flink 在事件时间加水印时如何处理延迟元素感到有些困惑。

我的理解是,当 Flink 读取一个数据流时,当看到任何事件时间大于当前 watermark 的数据时,watermark 时间就会增加。然后,任何覆盖时间严格小于水印的窗口都会被触发以进行驱逐(假设不是延迟允许。

但是,以这个最小的例子:

import org.apache.flink.api.scala._
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.scala.{StreamExecutionEnvironment}
import org.apache.flink.streaming.api.windowing.assigners.{TumblingEventTimeWindows}
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.util.Collector
import org.apache.log4j.{Level, Logger}

object EventTimeExample {

  Logger.getLogger("org").setLevel(Level.OFF)
  Logger.getLogger("akka").setLevel(Level.OFF)

  case class ExampleType(time: Long, value: Long)

  def main(args: Array[String]) {

    // Set up environment
    val env = StreamExecutionEnvironment.createLocalEnvironment(1)
    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)

    // Example S3 path
    val simple = env.fromCollection(Seq(
      ExampleType(1525132800000L, 1),
      ExampleType(1525132800000L, 2) ,
      ExampleType(1525132920000L, 3),
      ExampleType(1525132800000L, 4)
    ))
      .assignAscendingTimestamps(_.time)

    val windows = simple
      .windowAll(TumblingEventTimeWindows.of(Time.seconds(60)))
      .apply{
       (window, iter, collector: Collector[(Long, Long, String)]) => {
        collector.collect(window.getStart, window.getEnd, iter.map(_.value).toString())
      }
    }

    windows.print
    env.execute("TimeStampExample")
  }
}

运行结果是:

(1525132800000,1525132860000,List(1, 2, 4))
(1525132920000,1525132980000,List(3))

但是,如果我的理解是正确的,4不应该包含在这里的第一个窗口中,因为当达到3记录的值时应该更新水印时间。

现在我认识到这是一个微不足道的例子,但不理解这一点会使理解更复杂的流程变得困难。

【问题讨论】:

    标签: apache-flink watermark flink-streaming


    【解决方案1】:

    您的理解基本上是正确的,但是这里还有一些事情需要考虑。

    首先,您使用了assignAscendingTimestamps(),它只能在事件流完全有序(按时间戳)时使用,这里不是这种情况。运行此应用程序时,您应该会看到以下警告:

    WARN  org.apache.flink.streaming.api.functions.timestamps.AscendingTimestampExtractor  - Timestamp monotony violated: 1525132800000 < 1525132920000
    

    这里起作用的另一个因素是AscendingTimestampExtractor 不会为每个经过的流元素更新当前的水印。这是一个周期性水印生成器的示例,它将每 n 毫秒将Watermark 注入流中,其中 n 由ExecutionConfig.setAutoWatermarkInterval(...) 定义,默认为 200 毫秒。这就是事件 #4 潜入第一个窗口的方式。

    要获得您期望的结果,您可以实现一个标点水印生成器,配置为为每个事件生成一个水印:

    class PunctuatedAssigner extends AssignerWithPunctuatedWatermarks[ExampleType] {
      override def extractTimestamp(element: ExampleType, previousElementTimestamp: Long): Long = {
        element.time
      }
    
      override def checkAndGetNextWatermark(lastElement: ExampleType, extractedTimestamp: Long): Watermark = {
        new Watermark(extractedTimestamp)
      }
    }
    

    然后你会像这样使用它:

    val simple = env.fromCollection(Seq(
      ExampleType(1525132800000L, 1),
      ExampleType(1525132800000L, 2) ,
      ExampleType(1525132920000L, 3),
      ExampleType(1525132800000L, 4)
    ))
      .assignTimestampsAndWatermarks(new PunctuatedAssigner)
    

    现在您的示例产生了以下结果:

    (1525132800000,1525132860000,List(1, 2))
    (1525132920000,1525132980000,List(3))
    

    事件 #4 已被删除,因为它迟到了。这可以通过放松水印生成器来调整,以适应一些乱序。例如,

    override def checkAndGetNextWatermark(lastElement: ExampleType, extractedTimestamp: Long): Watermark = {
      new Watermark(extractedTimestamp - 200000)
    }
    

    然后产生这些结果:

    (1525132800000,1525132860000,List(1, 2, 4))
    (1525132920000,1525132980000,List(3))
    

    或者您可以配置窗口以允许延迟事件

    val windows = simple
      .windowAll(TumblingEventTimeWindows.of(Time.seconds(60)))
      .allowedLateness(Time.seconds(200))
      ...
    

    然后导致第一个窗口触发两次:

    (1525132800000,1525132860000,List(1, 2))
    (1525132800000,1525132860000,List(1, 2, 4))
    (1525132920000,1525132980000,List(3))
    

    请注意,由于处理水印会产生一些开销,因此您通常不希望以这种方式使用带标点符号的水印(每个事件都有一个水印)。对于大多数应用程序,基于BoundedOutOfOrdernessTimestampExtractor 的周期性水印是更好的选择。

    【讨论】:

    • 哇,感谢您的详细回答。我没有看到那个警告,因为我实际上在我发布的代码的开头关闭了警告:-) 但是 - 如果事件“4”真的迟到了,这个警告是否适用?对于处理我所拥有的更复杂案例的最佳方法,我仍然有点迷茫,但我会将其移至邮件列表。你的解释很有帮助。
    • 是的,警告适用。升序时间戳水印生成器不适用于时间戳不升序的情况。
    • “不打算”是指它实际上会行为不端吗?我不知道这是否真的会导致水印及时返回。
    • 水印不会及时返回;实施受到保护。但我不相信如何处理(或不处理)乱序事件的确切行为被指定或保证保持不变。
    • 嗯,好吧,我对此有点困惑。从我在源代码中可以看到,它只需要时间戳并提取它,但我会继续深入了解。
    【解决方案2】:

    如果使用了 BoundedOutOfOrdernessTimestampExtractor,则在新事件到来之前不会输出最后一个计算。如果我们在水印中使用 SystemTime,它可以工作,但是当您重新运行带有嵌入时间戳(过去事件)的消息时,它不会计算那些。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-19
      相关资源
      最近更新 更多