Flink 有狀態流式處理

目錄

傳統批次處理方法

理想方法

流式處理

分散式流式處理

有狀態分散式流式處理

狀態容錯(State Fault Tolerance)

分散式狀態容錯

分佈式快照(Distributed Snapshots)

狀態維護(State Management)

Event-time 處理

Event-Time 處理

Watermarks

狀態保存與遷移(Savenpoints and Job Migration)

保存點(SavePoint)


傳統批次處理方法

【1】持續收取數據(kafka等),以window 時間作爲劃分,劃分一個一個的批次檔案(按照時間或者大小等);

【2】週期性執行批次運算(Spark/Stom等);

傳統批次處理方法存在的問題:

【1】假設計算每小時出現特定事件的轉換次數(例如:1、2......),但某個事件正好處於1到2之間就尷尬了。需要將1點處理一半的結果帶到2點這個批次中進行運算。而這個劃分跟我們事件發生的時間也是有誤差的。
【2】在分佈式多線程的情況下,如果接收到事件的順序顛倒了,又該如何處理?

理想方法

累積狀態:表示過去歷史接收過的所有事件。可以是計數或者機器模型等等。


我們要處理一個持續維護的狀態時,最適合的方式就是狀態流處理(累積狀態和維護狀態+時間,是不是該收的結果都收到了)

【1】有狀態流處理作爲一種新的持續過程範式,處理連續的數據;
【2】產生準確的結果;
【3】實時可用的結果僅爲模型的自然結果;

流式處理

分散式流式處理


【1】從數據中選擇一個屬性作爲key 對輸入流進行分區;
【2】使用多個實例,每個實例負責部分 key的存儲,根據Hash值,相同的key一定落在相同的分區進行處理;

有狀態分散式流式處理

定義一個變量X,輸出結果依據這個X,這個X就是一個狀態。有狀態分散的流失處理引擎,當狀態可能會累計非常大。當key比較多的時候就會超出單臺節點的負荷量。這個x就應該有狀態後臺使用memory去維護它。【數據傾斜】

狀態容錯(State Fault Tolerance)

狀態掛了,如何確保狀態擁有精確一次(exactly-once guarantee)的容錯保證?就是通過通過定期的快照+事件日誌位置。我們先假設一個簡單的場景,如下,一個隊列在不斷的傳輸數據。單一的process 在處理數據。這個 process 沒處理一個數據都會累計一個狀態。如何爲這個process 做一個容錯。做法就是沒處理完一筆,更改完狀態之後,就做一次快照(包含它處理的數據在隊列中的位置和它處理到的位置以及當時的狀態進行對比)

舉個例子:如下我處理到第二筆數據,我就會記錄下第二個位置在進入 process 之前的信息(位置X+狀態 @X

當進入 process 處理的時候出現了 fail時,Flink就會根據上一次的位置+狀態進行恢復。

如何在分散式場景下替多個擁有本地狀態的運算子產生一個全域一致的快照global consistent snapshot)?
方式一:更改該任務流過的所有運算子的狀態。比較笨,有一個副作用,就是我處理完這筆數據,它應該就到了一個process,我本應該做其他數據的處理了,可是爲了全局一致性快照就會停止前面和當前的 process的運算來保證全局一致性。

分散式狀態容錯

通過 checkpoint 實現分散式狀態容錯

每一個運算子它本地都有一個維護一個狀態,當要產生一個檢查點(checkpoit)的時候,都會將這個檢查點存儲在一個更小的分佈式文件系統DFS中。當出現某個算子 fail之後,就會從所有的checkpoint 中獲取所有算子的上一個狀態進行恢復。把消息隊列的位置也進行恢復。也就是多線程工作,每一個任務在DFS中就可以看作一個線程,它們數據存儲的key就是這個任務,每一個算子的處理狀態都會按照處理順序添加進去。

分佈式快照(Distributed Snapshots)

更重要是時如何在不中斷運算的前提下生成快照?其實就是給每一個任務標記一個checkpoint n 不同的任務這個 n是不同的,相同的任務在不同的算子裏面它是相同的。具體我們把這個分解後看看。

【1】如下圖,當我們從數據源獲取數據的時候,其實我們已經開始有狀態了,這個時候我們可以把任務處理的整個過程抽象成如下圖中的一張表。

【2】首先是數據源的狀態,就是數據在操作前的一個位置offset進行快照存儲,如下圖所示:

【3】當獲取到數據源之後,就進入算子中進行處理,此時就會對數據進入之前的狀態進行checkpoint。記錄一個savepoint。

【4】在最後一次操作前(輸出)也會記錄 checkpoint。在這個過程中,其實前面的算子也在產生不同的 checkpoint n-1 等。如果要進行恢復使用的話,必須是一個complete 完整的 Checkpoint。只有部分數據的Checkpoint是不能使用的。

狀態維護(State Management)

本地維護的這個狀態可能非常非常大。後端的管理系統一般使用內存維護這些狀態。

Flink 提供了兩種狀態後端:JVM Heap 狀態後端,適合比較小的狀態,量不要很大。當運算子action要讀取狀態的時候,都是一個Java對象的read 或者 write。當要產生一個檢查點的時候,需要將每個運算子的本地狀態數據通過序列化存儲在DFS中,

當狀態非常大的時候就不能使用 JVM Heap 的時候,就需要用到 RocksDB。當算子需要讀取的時候本地state的時候需要進行序列化操作從而節省內存,同時,當需要進行checkpoint 到DFS時,也少了序列化的步驟。它也會給本地存儲一份,當fail的時候就可以很快恢復,提高效率。

Event-time 處理

EventTime 是事件產生的時間

下面是一張,程序處理時間與事件發生時間的時間差的一張對比圖來更好的理解EventTime。

Event-Time 處理

也就是說我們要統計的3-4點之間的數據,程序4點結束這個執行不是根據window時間,而是根據event-Time。

Watermarks

Flink 是watermarks 實現 Event-Time 功能的。在 Flink 裏面也屬於一個特殊事件,精髓是當某個運算子收到一個帶有時間戳t的watermark 後就不會再收到任何小於該時間戳的事件了。也就是當 window需要統計4點的數據時,例如我們每5分鐘發一次watermark ,那麼當 window收到4.05的watermark 的時候纔會去統計4點之前的數據(下一次)。如果4.05收到了4點之前的數據的話,Flink1.5 會把這個事件輸出到旁路輸出(side output),你可以獲取出來,進行處理。目前有一個問題就是:如果某個Stream Partition 沒有輸入了,也就沒有 Watermarks。那麼 window 就沒辦法進行處理了。當多個數據流的 watermarks 不相同的時候,Flink 會取最小的watermarks 進行運算。可以在接收到資源的時候通過代碼設置 watermarks。

OutputTag<String> outputTag = new OutputTag<String>("side-output") {};

狀態保存與遷移(Savenpoints and Job Migration)

流失處理應用是無時無刻在運行,運維上有幾個重要的考量:如何遷移狀態
【1】更改應用邏輯/修改bug等,如何將前一個執行的狀態遷移到新的上面執行。
【2】如何重新定義運行的平行化程度。
【3】如何升級運算Flink 的版本號。

保存點(SavePoint)

可以想成:一個手動產生的檢查點(CheckPoint):保存點記錄某一個流失應用中的所有運算中的狀態。當觸發 SavePoint之後,Flink提供了兩種選擇停止消費或者繼續運算,根據場景定義。

執行停止之前,產生一個保存點。就可以解決上面提到的3個問題。

從保存點恢復新的執行,這個時候,例如我們重啓花了30分鐘,這段事件kafka 還在不斷的接收新的數據。恢復之後,Flink就需要從當時記錄的kafka位置趕上最新的位置。這個時候利用Event-Time 處理新的數據都是事件發生時的數據,這個時候再跟程序執行的時間比較就更能體現 Event-time的價值。


----架構師資料,關注公衆號獲取----

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章