[英]Processing time windows doesn't work on finite data sources in Apache Flink
我正在嘗試將一個非常簡單的窗口函數應用於 Apache Flink 中的有限數據流(本地,無集群)。 這是示例:
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 秒,並且任務在窗口的計時器觸發之前關閉。 調試顯示到達ProcessingTimeTrigger
中的定時器設置行。 難道所有啟動的計時器不應該在任務關閉之前完成(至少這是我從代碼中得到的印象)?
你能幫我理解這一點並使之更具確定性嗎?
2018 年 9 月 23 日更新 #1:
我還試驗了事件時間窗口而不是處理時間窗口。 如果我這樣做:
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()
然后再次沒有打印任何內容。 調試器顯示為每個元素調用觸發器的onElement
,但從未調用onEventTime
。
另外,如果我修改時間戳提取器以進行更大的步驟:
element.charAt(0).toInt * 1000
除了最后一個之外,所有元素都被打印出來(每組一個元素,這是預期的)。
2018 年 9 月 23 日更新 #2:
在此評論中回答了更新 #1。
當有限源到達末尾時,如果您使用事件時間,則將注入時間戳為 Long.MAX_VALUE 的水印,這將導致所有事件時間計時器觸發。 但是,隨着處理時間的增加,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 處理有限流的方法。 所有人都面臨如何正確結束這項工作的問題(源停止,操作員處理所有數據,接收器完成)。 因為處理時間和計數窗口只會像大衛的回答那樣不處理窗口/數據。 一種方法是同步源和操作員之間的通信,然后通知退出。 但這並不美麗。 所以只需簡單地選擇 EventTime 窗口,即使在第一次開始停止源后,它也會處理所有數據。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.