天天看點

Flink流處理(四)- 時間語義

4. 時間語義(Time Semantics)

這章我們會介紹時間語義,以及在流中,對于時間的各種不同的概念的描述。同時我們也會讨論一個流處理器在事件亂序的情況下,如何能提供精準的結果,以及如何使用流對曆史events進行處理。

一分鐘的含義

假設我們要持續的對流計算并生成結果,例如每一分鐘。這裡的“一分鐘”在流處理應用裡到底意味着什麼呢?

考慮這麼一個場景,一個程式用于分析手機網遊使用者的events。使用者屬于各個小隊。基于小隊成員達成遊戲給定目标的速度,應用收集小隊的資訊并在遊戲中給出獎勵,例如更新、經驗等。例如,如果一個小隊的所有使用者在一分鐘内生産了500個泡泡,則他們獲得一次更新。小明是其中一個玩家,他每天早上會在去上班的公交上玩這個遊戲。可問題是,小明住在大山裡,在出山的路上網絡特别差。假設小明開始在遊戲中生産泡泡,此時手機連接配接到了網絡,并發送events到分析程式。突然,公交進入了一個隧道,導緻他的手機斷網了。小明繼續玩遊戲,此時events會緩存到他的手機裡。當公交出了隧道,網絡恢複後,pending的events會被發送到分析程式。此時分析程式怎麼做?在這裡一分鐘意味着什麼呢?是否考慮到了小明斷網的時間?下圖描述了此問題:

Flink流處理(四)- 時間語義

線上遊戲是一個簡單的場景,描述了 operator 語義如何依賴于時間。當events發生在某個時間段内,但是應用接收到events時已超過了這個時間段時,應用應如何處理時間語義?在這個手機遊戲中,若是對此處理不當,會導緻較差的客戶體驗。但是在一些非常看重時間的應用中,結果可能會更嚴重。如果僅考慮在一分鐘内接收到多少個events,則結果會直接與于網絡連接配接狀況、或是處理速度等相關。是以,真正定義一分鐘内的events時,應該與資料本身的時間相關。

例如在小明玩遊戲這個例子中,流處理應用可以以兩種時間概念進行操作:處理時間(processing time)或事件時間(event time)。接下來介紹這兩種概念。

處理時間(Processing Time)

處理時間是處理流的operator在執行時,所屬機器上的本地時間。Processing-time 視窗包含所有在一個時間周期内到達window operator 的events,以本地機器時間衡量。如下圖所示,在小明遊戲的案例中,在小明的手機斷開連接配接後,processing-time 視窗仍會持續計時,是以不會将小明斷網時間内的events計入到目前時間段。

Flink流處理(四)- 時間語義

事件時間(Event Time)

事件時間是一個event實際發生的時間,它基于事件流中event被打上的時間戳。時間戳一般存儲與event 資料中。下圖展示了一個event-time 視窗,可以正确的将events放入合适的視窗中,反應了事件實際發生的情況,即使事件的到達存在延遲。

Flink流處理(四)- 時間語義

事件時間将處理速度與結果完全解耦。基于事件時間的操作時可預測的(predictable)并且結果是明确的(deterministic)。使用事件時間視窗計算時,無論流處理的速度有多快,或是events到達operator的速度有多慢,它輸出的一定是個相同的結果。

處理延遲的events僅僅是事件時間可以解決的衆多問題中的一個。更普遍的資料亂序的問題,也可以由事件時間解決。假設小花是遊戲的另一個玩家,她與小明都是做的同一班公交,在同一時間玩遊戲,但是用的是不同的手機卡。在過隧道時,小明的手機沒信号了,但是小花的手機還可以正常聯網并向遊戲應用繼續發送events。

依賴于事件時間,我們可以在資料無序到達的情況下,依然保證事件的正确性。進一步的說,在結合了可重跑的流(replayable streams)時,明确的時間戳可以用于快速執行過去的資料(fast forward the past)。也就是說,你可以重跑(replay) 一個流并分析曆史資料,就像events是實時發生的一樣。同時,你也可以快進計算,直到到達目前的狀态(fast forward the computation to the present),這樣一旦應用擷取了正在發生的事件時,它可以像一個實時應用一樣,以同樣的program logic繼續工作。

水印(Watermarks)

在讨論event-time 視窗時,我們忽略了一個很重要的方面:我們如何決定何時觸發一個event-time 視窗?也就是說,我們需要等待多長時間,才能確定已經收到了在某個特定時間點之前發生的所有事件呢?并且我們如何知道資料會有延遲呢?很遺憾,這些問題是沒有一個完美的答案的,因為分布式系統會有各種預料之外的異常,并且可能會有多方面的外部元件影響延遲。在這章我們會引入如何使用水印(watermarks)配置event-time視窗的行為。

水印是一個全局的進度名額(progress metric),表示的是:在什麼時間點,我們可以有信心判斷,之後不會有更多的(延遲的)事件到達。本質上,水印提供了一個邏輯時鐘,提醒系統關于目前event time的資訊。當一個operator收到了一個水印,時間為T。它可以假設:不會再有更多時間戳小于T的事件被收到。水印對于event-time視窗以及operators處理亂序events至關重要。一旦一個水印被接收到後,operators即被通知到:對于某個時間周期,所有的時間戳已經被觀察到(observed),接下來應觸發計算,或是将收到的事件進行排序。

水印提供了一個配置,用于權衡結果可信度與延遲。Eager watermarks可以確定低延遲,但是提供較低的可信度。在這種情況下,延遲的events可能會在水印之後到達,我們需要提供處理這些events的代碼。另一方面,如果水印特别寬松,則我們對結果的準确度會有更大的信心,但是可能會增加不必要的處理延遲。

在很多真實應用中,系統并沒有足夠的知識去完美地準确算出水印。在手機遊戲的例子中,基本是不可能預測使用者會有多長的時間丢失連接配接。無論是自定義的水印或是自動生成的水印,在分布式系統中,由于有落後的tasks,跟蹤整個分布式系統的進度可能仍是個問題。是以,如果僅簡單的依賴于水印,則可能并不是一個很好的方法。更重要的是,流處理系統應提供一些機制去處理這些落後于水印的events。根據應用的需求,可以簡單的丢棄這些事件,或是記錄日志,亦或是使用它們去修正之前的結果。

處理時間vs事件時間

這裡你可能會好奇既然event time可以解決我們所有的問題,那為什麼還要提出processing time。事實上,處理時間在某些情況下是非常有用的。Processing-time 視窗可以盡可能地引入最低延時。例如:不考慮延遲及亂序事件,window僅是用于緩存一些資料,在到達一段時間後立即觸發一個計算。是以對于那些速度比準确度更重要的應用來說,processing time 會更友善。另一個案例是定期實時提供報告結果,不考慮精準度。最後,processing-time視窗是流自身行為的一個展現,可能在某些場景下是一個很好的屬性。例如,計算每秒收集到的事件,用于檢測outage。總而言之,processing time 提供了低延時,但是輸出的結果取決于處理的速度,并且結果并不是明确的。而event time保證了明确的結果,并可以處理延時或亂序的事件。

References:

Vasiliki Kalavri, Fabian Hueske. Stream Processing With Apache Flink. 2019