Apache flink Flink水印和触发器-未在事件时间丢弃延迟元素?

Apache flink Flink水印和触发器-未在事件时间丢弃延迟元素?,apache-flink,watermark,flink-streaming,Apache Flink,Watermark,Flink Streaming,我对Flink在活动时间添加水印时如何处理后期元素感到有些困惑 我的理解是,当Flink读取数据流时,水印时间在看到任何事件时间大于当前水印事件时间的数据时进行。然后,任何覆盖时间严格小于水印的窗口都会被触发,以进行驱逐(假设没有延迟津贴) 但是,以这个最小的例子为例: import org.apache.flink.api.scala._ import org.apache.flink.streaming.api.TimeCharacteristic import org.apache.fli

我对Flink在活动时间添加水印时如何处理后期元素感到有些困惑

我的理解是,当Flink读取数据流时,水印时间在看到任何事件时间大于当前水印事件时间的数据时进行。然后,任何覆盖时间严格小于水印的窗口都会被触发,以进行驱逐(假设没有延迟津贴)

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

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
记录时,应更新水印时间


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

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

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

WARN  org.apache.flink.streaming.api.functions.timestamps.AscendingTimestampExtractor  - Timestamp monotony violated: 1525132800000 < 1525132920000
然后,您将使用如下方式:

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))
(1525132800000,1525132860000,List(1, 2, 4))
(1525132920000,1525132980000,List(3))
事件#4已被删除,因为它已延迟。这可以通过放松水印生成器进行调整,以适应一定数量的无序情况。例如

override def checkAndGetNextWatermark(lastElement: ExampleType, extractedTimestamp: Long): Watermark = {
  new Watermark(extractedTimestamp - 200000)
}
然后产生以下结果:

(1525132800000,1525132860000,List(1, 2))
(1525132920000,1525132980000,List(3))
(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))

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

如果使用BoundedAutofordernessTimestampExtractor,则直到新事件出现时才输出最后一次计算。如果我们在水印中使用SystemTime,它会工作,但当您重新运行带有嵌入timesta的消息时mp(过去的事件)它不计算这些。

哇,谢谢你的详细回答。我没有看到这个警告,因为我实际上在我发布的代码的开头关闭了警告:-)但是-如果事件“4”真的迟到了,这个警告适用吗?我对处理更复杂的案件的最佳方法仍然有点迷茫,但我会把它转移到邮件列表中。你的解释很有帮助。是的,警告是适用的。升序时间戳水印生成器不适用于时间戳不升序的情况。您所说的“非预期”是指它实际上会出现错误行为吗?我不知道这是否真的会导致水印返回时间;实现受到保护,不受此影响。但我不相信无序事件将如何处理(或不处理)的确切行为是指定的,也不保证保持不变。嗯,好吧,我对此有点困惑。从我在源代码中看到的情况来看,它只是获取时间戳并提取它,但我会继续挖掘以理解它。