Apache flink 处理时间窗口不为';在ApacheFlink中,您无法处理有限的数据源

Apache flink 处理时间窗口不为';在ApacheFlink中,您无法处理有限的数据源,apache-flink,flink-streaming,Apache Flink,Flink Streaming,我正在尝试将一个非常简单的窗口函数应用于ApacheFlink中的有限数据流(本地,无集群)。下面是一个例子: val env = StreamExecutionEnvironment.getExecutionEnvironment env .fromCollection(List("a", "b", "c", "d", "e")) .windowAll(TumblingProcessingTimeWindows.of(Time.seconds(1))) .trigger(Proc

我正在尝试将一个非常简单的窗口函数应用于ApacheFlink中的有限数据流(本地,无集群)。下面是一个例子:

val env = StreamExecutionEnvironment.getExecutionEnvironment
env
  .fromCollection(List("a", "b", "c", "d", "e"))

  .windowAll(TumblingProcessingTimeWindows.of(Time.seconds(1)))
  .trigger(ProcessingTimeTrigger.create)
  .process(new ProcessAllWindowFunction[String, String, TimeWindow] {
    override def process(context: Context, elements: Iterable[String], out: Collector[String]): Unit = {
      out.collect(elements.toList.sorted.toString())
    }
  })

  .print()

env.execute()
在这里,我尝试将在一秒钟内到达窗口的所有元素分组,然后只打印这些组

我假设所有元素都将在不到一秒钟的时间内生成并进入一个窗口,因此
print()
中将有一个传入元素。但是,当我运行此操作时,根本不会打印任何内容

如果我把窗户上的东西都拿走,比如

val env = StreamExecutionEnvironment.getExecutionEnvironment
env
  .fromCollection(List("a", "b", "c", "d", "e"))
  .print()
我看到运行后打印的元素。我也尝试了这个与文件源,没有区别

我的机器上的默认并行度是6。如果我用并行度和延迟来做实验,像这样

val env = StreamExecutionEnvironment.createLocalEnvironment(2)
env
  .fromCollection(List("a", "b", "c", "d", "e"))
  .map { x => Thread.sleep(1500); x }
我能够将一些(不是全部)元素分组,然后打印出来

我的第一个假设是源代码完成的速度比1秒快得多,并且任务在窗口计时器触发之前关闭。调试表明达到了预期目标。难道不是所有启动的计时器都应该在任务关闭之前完成吗(至少这是我的印象)

你能帮我理解这一点,让它更具确定性吗

更新#12018年9月23日:

我还试验了事件时间窗口,而不是处理时间窗口。如果我这样做:

val env = StreamExecutionEnvironment.getExecutionEnvironment
env
  .fromCollection(List("a", "b", "c", "d", "e"))
  .assignTimestampsAndWatermarks(new AscendingTimestampExtractor[String] {
    override def extractAscendingTimestamp(element: String): Long = {
      element.charAt(0).toInt
    }
  })

  .windowAll(TumblingEventTimeWindows.of(Time.seconds(1)))
  .trigger(EventTimeTrigger.create)
  .process(new ProcessAllWindowFunction[String, String, TimeWindow] {
    override def process(context: Context, elements: Iterable[String], out: Collector[String]): Unit = {
      out.collect(elements.toList.toString())
    }
  })

  .print()

env.execute()
然后再次没有打印任何内容。调试器显示为每个元素调用触发器的
oneelement
,但从不调用
onEventTime

此外,如果我修改时间戳提取器以执行更大的步骤:

element.charAt(0).toInt * 1000
除最后一个元素外,所有元素都将打印(每组一个元素,这是预期的)

更新#22018年9月23日:


更新#1在中得到响应。

当有限源到达末尾时,如果您使用的是事件时间,则将注入带有timestamp Long.MAX_值的水印,这将导致触发所有事件时间计时器。然而,随着处理时间的推移,Flink将等待所有当前启动的计时器完成其操作,然后退出

正如您所怀疑的,您没有看到任何输出,因为源代码完成得非常快

确定性行为与事件时间处理是直接的;随着处理时间的推移,这并不是真正可以实现的

但这里有一个或多或少能奏效的技巧:

val env = StreamExecutionEnvironment.getExecutionEnvironment

val s = env.fromCollection(List("a", "b", "c", "d", "e"))
val t = env.addSource((context: SourceContext[String]) => {
  while(true) {
    Thread.sleep(100)
    context.collect("dummy")
  }
})

s.union(t)
  .filter(_ != "dummy")
  .windowAll(TumblingProcessingTimeWindows.of(Time.seconds(1)))
  .process(new ProcessAllWindowFunction[String, String, TimeWindow] {
    override def process(context: Context, elements: Iterable[String], out: Collector[String]): Unit = {
      out.collect(elements.toList.sorted.toString())
    }
  })
  .print()

env.execute()

大卫的回答很直截了当!
我尝试过用ProcessTime或GlobalWindows处理有限流的方法。所有人都面临如何正确结束这项工作的问题(源停止、操作员处理所有数据、接收器完成)。因为processtime和count窗口只会像David的回答一样保留未处理的窗口/数据。一种方法是同步源和操作员之间的通信,然后通知退出。但它并不漂亮。因此,只需选择EventTime窗口,即使在第一次开始停止源代码之后,该窗口也会处理所有数据。

你好,David,感谢您的回答和帮助。至于活动时间,请参见帖子中的更新。之后,它似乎起作用了!谢谢你,大卫!谢谢你的提问和回答,因为我有同样的困惑。那么David说因为源完成了,而窗口还没有启动,它会在获得机会之前终止?这是故意的行为吗?我还尝试将allowedLateness设置为无效,但出于同样的原因,我假设是这样。请注意,您不需要指定触发,因为您在每种情况下都使用默认触发器。处理事件时间时,必须包括env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);太好了,现在可以了。这是否意味着在一个环境中只能使用一个时间特征?如果我需要以处理时间的方式处理另一个流怎么办?请提供一个可复制的代码和您的答案?