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