Flink 如何現實新的流處理應用第一部分:事件時間與無序處理

語言: CN / TW / HK

流資料處理正處於蓬勃發展中,可以提供更實時的資料以實現更好的資料洞察,同時從資料中進行分析的流程更加簡化。在現實世界中資料生產是一個連續不斷的過程(例如,Web伺服器日誌,移動應用程式中的使用者活躍,資料庫事務或者感測器讀取的資料)。正如其他人所指出的,到目前為止,大部分資料架構都是建立在資料是有限的、靜態的這樣的基本假設之上。為了縮減連續資料生產和舊”批處理”系統侷限性之間的這一根本差距,引入了複雜而脆弱(fragile)的端到端管道。現代流處理技術通過以現實世界事件產生的形式對資料進行建模和處理,從而減輕了對複雜解決方案的依賴。

以流的方式對資料建模並處理的想法並不新鮮。但是,新的流處理系統(包括 Apache Flink)與舊的流處理系統(包括開源和專有的)有本質的區別。使用 Flink 進行資料流處理比傳統概念上快速(實時)分析要應用廣泛得多,包括對歷史資料的分析,以及支援新一類的應用程式(使用以前的舊技術很難或根本不能實現這些應用程式)。我們將詳細研究一些應用程式,並展示 Flink 是如何以及為何能夠有效地支援這些應用程式:

  • 亂序資料上的準確結果。在大多數流處理場景中,事件的順序非常重要,通常事件到達資料處理叢集的順序與它在現實世界中實際發生的時間不同。Flink 是第一個可以讓開發人員可以控制事件時間(事情實際發生的時間)的開源系統,並可以在亂序流上獲得準確的結果。

  • 會話和非對齊視窗:對 Web 日誌、機器日誌以及其他資料進行分析需要能夠在會話中將事件進行分組。會話是非對齊視窗的一個典型例子,例如,每個 key 的視窗開始和結束都不一樣,這需要 Flink 提供的視窗和檢查點之間的分離。

  • 應用程式狀態版本控制:在純資料流體系結構(通常稱為 Kappa 體系結構)中,流是事件的持久記錄,應用程式使用從流中計算出的狀態進行工作。在這樣的體系結構中,Flink 的分散式快照可用於’版本化’應用程式狀態:可以升級應用程式而不會丟失瞬態狀態,應用程式狀態可以回滾到以前的版本(例如,發現並糾正錯誤)或者應用程式的不同變體可以被分離出某個狀態(例如,用於 A/B 測試)。

1. 亂序資料流和事件時間視窗

在討論亂序資料流處理之前,我們需要定義順序以及時間。流處理有兩種時間概念:

  • 事件時間是事件在現實世界中發生的時間,通常由事件發出的資料記錄上的時間戳表示。在幾乎所有的資料流中,事件都帶有表示事件產生時間的時間戳:Web伺服器日誌,來自監視代理的事件,移動應用日誌,感測器資料等。

  • 處理時間是處理事件的運算元所在機器上的本地時鐘時間。

在許多流處理中,在應用程式(伺服器日誌,感測器,監視代理等)產生事件的時間與其到達訊息佇列中進行處理的時間有一定延遲。原因有很多:

  • 在不同的網路路徑上有不同的延遲

  • 來自消費者的排隊和背壓影響

  • 資料峰值速率

  • 一些事件的生產者並不總是處於連線狀態中(移動裝置,感測器等)

  • 一些傳送爆發性事件的生產者

這樣產生的影響是事件在佇列中相對於事件時間通常是無序的。此外,事件產生的時間戳與其到達佇列或流處理器時間的差異隨著時間而發生變化。這通常被稱為事件時間偏差,並被定義為 ‘處理時間 - 事件時間’。

Flink 允許使用者定義基於事件時間的視窗,而不是處理時間。這樣的視窗不會輕易被亂序事件和不同事件時間偏差而影響。Flink 使用事件時間時鐘來追蹤事件時間,並通過 Watermark 來實現。Watermark 是 Flink 資料來源生成的一種特殊事件,可以粗略地評估事件時間。時間為 T 的 Watermark 表示事件時間在該流(或分割槽)上已經處理到時間 T,這意味著不會再有時間戳小於 T 的事件到達了。Flink 運算元可以根據這個時鐘跟蹤事件時間。下圖展示了 Flink 如何基於事件時間來計算視窗。觀察到的會有多個視窗在同時執行(當出現亂序時),並根據事件時間戳把事件分配給對應的視窗。在 Watermark 到達時會觸發視窗計算並更新事件時鐘。

基於事件時間的管道會盡可能快的產生精確的結果(一旦事件時間到達指定時間),但必要的時候儘可能的延遲(把相關事件儘可能的都包含進來)。與使用批處理器週期性計算聚合相比,流式事件時間管道可以提前產生結果並且更精確(因為批處理管道不能正確處理跨批次的亂序事件)。最後,流式作業簡單而明確地描述瞭如何根據時間(視窗)對元素進行分組,如何及時評估必要的進度(Watermark),而不是像批處理其通過滾動接收檔案、批量作業以及定期作業排程程式實現。

2. 整合事件時間和實時管道

事件時間管道會產生一定的延遲,因為需要等待所需的事件全部到達。在某些情況下,上述延遲太大以至於無法產生準確的實時結果。因為 Flink 是一個合適的流處理器,可以在幾毫秒內處理完事件,所以很容易就可以在同一個程式中將低延遲的實時管道與事件時間管道結合起來。下面的例子展示了一個生產程式:

  • 基於單個事件實現低延遲警報。如果發現某種型別的事件,則傳送警報訊息。

  • 基於處理時間視窗的實時儀表板,每隔幾秒就對事件進行聚合和計數。

  • 根據事件時間準確統計。

整合事件時間和處理時間的另一種方式是定義具有提前輸出結果以及最大延遲的事件時間視窗:

  • 事件時間視窗可以自定義一個滯後於處理時間的最大延遲。例如,一個事件時間視窗將在事件時間 10:15h 關閉,可以自定義在處理時間內不晚於 10:20h 關閉。

  • 事件時間視窗可以提前輸出結果。例如,計算 15 分鐘事件時間滑動視窗中的事件數量的程式,可以在按處理時間每分鐘輸出當前每個未觸發視窗的計數。

3. Flink 如何度量時間

現在,我們深入瞭解 Flink 時間處理的機制,以及這些機制與舊式流式處理系統有什麼不同之處。一般來說,時間使用時鐘度量的。流式作業叢集機器的內部時鐘是最簡單的時鐘(稱為掛鐘),時鐘可以記錄處理時間。為了追蹤事件時間,我們需要一個時鐘來度量不同機器上的同一時間。這可以通過 Flink 的 Watermark 機制來完成。Watermark 是一種特殊事件,表示指事件流中的時間(即事件流中的真實世界時間戳)到達了一個特定時間點(例如,10am),並且從現在起不會有早於上午 10 點時間戳的事件到達。這些 Watermark 作為資料流的一部分與常規事件一起流轉,Flink 運算元一旦從所有上游運算元/資料來源接收到 10am 的 Watermark,就將其事件時間提至上午10點。需要注意的是,基於事件時鐘追蹤時間比掛鐘粒度更粗,但更為正確,因為它在機器間保持一致。第三種類型的時鐘(我們稱之為系統時鐘)被流處理系統用於內部記賬,最重要的是能保證一致的語義(“精確一次處理”)。Flink 通過向資料流注入柵欄 Barriers 並生成一致性快照來跟蹤作業的進度。Barriers 類似於 Watermark,都是流經資料流的事件。不同之處在於 Barriers 不是由真實世界的資料來源產生的,而是根據 Flink Master 的掛鐘度量的。類似地,Spark Streaming 基於 Spark 的接收器的掛鐘排程微批次。Flink 的快照機制和 Spark 的微批處理機制都是系統時鐘的例子,這是一種追蹤計算時間(以及進度)的方法。如下展示了假設我們”凍結”計算下不同時鐘度量的不同的時間:

從上面可以看出,作業由一個數據源和一個視窗運算元組成,在兩臺機器上(worker 1 和 worker 2)上並行執行。事件中的數字表示時間戳,框的顏色表示進入不同的視窗中(灰色事件進入視窗1,紫色事件進入視窗2)。資料來源從訊息佇列中讀取事件,根據 key 進行分割槽,並將它們分發到正確的視窗運算元例項中。這裡的視窗是基於事件時間的時間視窗。我們看到,由於機器間時間不同步,不同機器(worker 1,worker 2 和 master)上在同一時刻的掛鐘度量成了不同的時間(分別為 10、8 以及 7,假設時間從 0 開始)。

資料來源發出 Watermark,目前時間戳為 4 的 Watermark 都已到達視窗運算元。這意味著事件時間時鐘度量為 4,而且該時間在平行計算中是一致的。最後,Master(JobManager)當前正在資料來源注入 Barrier 來獲取計算快照。這意味著系統時間度量為 7(這可能是第 7 個檢查點,也可以是基於 Master 處理時間注入的時間戳)。所以,我們已經看到流處理場景中存在三個時鐘:

  • 事件時鐘(粗略)度量事件流中的時間

  • 系統時鐘度量計算的進度,並在系統內部使用以在發生故障時提供一致的結果。這個時鐘實際上是基於協調機器的掛鐘。

  • 機器的掛鐘度量處理時間。

舊流式系統的一個常見缺陷是三個時鐘都是相同的。用相同的時鐘來度量現實世界中的時間,以及跟蹤計算的進度。這會導致兩個問題:

  • 計算結果不正確:由於事件在現實世界中發生的順序與其被攝取或處理的順序不同,因此係統可能會將事件分組到錯誤的時間視窗中。

  • 計算結果取決於當前時間,例如,流處理作業實際開始的那一天,以及機器度量的時間。

  • 系統配置引數會影響程式的語義:當增加檢查點間隔時,例如,為了增加吞吐量,windows 會緩衝更多元素。

舊流式系統的這些缺點使得它們很難用於需要準確(或至少可控準確)結果的應用程式以及需要處理歷史和實時資料的應用程式。再加上早期流式系統的吞吐量相對較低,這給流技術帶來了’不好的聲譽’:人們認為只有批處理才能實現重量級而準確的處理,而流系統只能實現一些快速的近似結果,例如,作為 Lambda 架構的一部分。Flink 的一個新功能是完全分離了這三個時鐘:

  • 基於 Watermark 的事件時鐘跟蹤事件流時間,並允許使用者根據事件時間定義視窗。當系統知道該視窗沒有進一步的事件會到達時,這些視窗會關閉。例如,當系統知道流中的事件時間已經進展到至少 10:15h 時,從 10:00h 到 10:15h 的時間視窗將會關閉。

  • 與事件時間時鐘完全分離的系統時鐘跟蹤計算進度併為全域性快照計時。此時鐘不向使用者 API 顯示,但用於協調分散式一致性。

  • 機器的掛鐘(處理時間)向用戶暴露,以支援處理時間視窗以及實現提早近似結果的事件時間視窗。

這種時鐘和時間進度的分離使得 Flink 比舊的’實時’流系統具有更多的能力。

4. 結論

通過這篇文章,我們可以瞭解到:

  • Flink 提供了基於事件時間觸發的視窗運算元,而不是基於機器的掛鐘時間觸發,所以即使在無序流或事件延遲時也能產生準確的結果。

  • Flink 將事件時間運算元與觸發器結合起來可以獲得提早結果和低延遲報警。

  • Flink 將跟蹤檢查點進度的內部系統時鐘與跟蹤事件時間的時鐘區分開。

原文:How Apache Flink™ Enables New Streaming Applications, Part 1