天天看點

Apache Flink資料流的Fault Tolerance機制簡介

Apache Flink提供了一個失敗恢複機制來使得資料流應用可以持續得恢複狀态。這個機制可以保證即使線上環境的失敗,程式的狀态也将能保證資料流達到exactly once的一緻性。注意這裡也可以選擇降級到保證at least once的一緻性級别。

失敗恢複機制持續地建構分布式流式資料的快照。對于那些隻有少量狀态的流處理應用,這些快照都是非常輕量級的并且可以以非常頻繁的頻率來建構快照而不需要太多地考慮性能問題。而流應用的狀态被存儲在一個可配置的持久化存儲(比如master節點或者HDFS)。

在程式失敗的情況下(比如由于機器、網絡或者軟體失敗),Flink将停止分布式流處理。系統将重新開機<code>operator</code>并且将他們重置為最新成功了的檢查點。輸入流會被重置為狀态快照點。任何被重新開機的并發資料流處理的記錄,可以得到的保證是:他們不可能是檢查點之前的記錄。

注意:對于該機制,為了達到完整的保證,資料流source(例如message queue或者message broker)需要具備回退到最近定義的還原點的能力。Apache Kafka具備這樣的能力并且Flink的Kafka連接配接器利用了這個能力。

因為Flink的檢查點是通過分布式快照實作的,是以這裡我們對快照和檢查點不進行區分。

Barriers:此處統一稱為屏障也可稱之為栅欄

在Flink的分布式快照機制中有一個核心的元素是流屏障。屏障作為資料流的一部分随着記錄被注入到資料流中。屏障永遠不會趕超通常的流記錄,它會嚴格遵循順序。屏障将資料流中的記錄隔離成一系列的記錄集合,并将一些集合中的資料加入到目前的快照中,而另一些資料加入到下一個快照中。每一個屏障攜帶着快照的ID,快照記錄着ID并且将其放在快照資料的前面。屏障不會中斷流處理,是以非常輕量級。來自不同快照的多個屏障可能同時出現在流中,這意味着多個快照可能并發地發生。

在<code>stream source</code>中,流屏障被注入到并發資料流中。快照n被注入屏障的點(簡稱為Sn),是在<code>source stream</code>中的資料已被納入該快照後的位置。例如,在Apache Kafka中,該位置将會是<code>partition</code>中最後一條記錄的<code>offset</code>。這個Sn的位置将被報告給檢查點協調器(<code>Flink JobManager</code>)。

屏障接下來會流向下遊。當一個中間的<code>operator</code>從所有它的輸入流中接收到一個來自快照n的屏障,它自身發射一個針對快照n的屏障到所有它的輸出流。一旦一個<code>sink operator</code>(流DAG的終點)從它所有的輸入流中接收到屏障n,它将會像檢查點協調器應答快照n。在所有的sink應答該快照後,它才被認為是完成了。

當快照n完成後,可以認為在Sn之前的記錄沒有必要再從<code>source</code>中流入,因為這些記錄已經穿過了整個資料流的處理拓撲。

那些不止一個輸入流的的<code>operator</code>需要在快照屏障上對齊(align)輸入流。上面的插圖說明了這一點:

一旦<code>operator</code>從外來流中收到快照屏障n,它就不能處理該流中更多的記錄直到它從其他輸入中接收到屏障n。否則,會混合屬于快照n以及快照n+1的記錄

彙報過屏障n的流會被臨時擱置到一邊,從這些流中繼續接收到的記錄并沒有被處理,而是被放進一個輸入緩沖區中

一旦最後一個流接收到屏障n,<code>operator</code>發射所有待處理的需要流出的記錄,然後發射快照n屏障本身

此後,<code>operator</code>恢複從所有輸入流的記錄的處理,在處理來自流的記錄之前先處理來自輸入緩沖區的記錄

無論<code>operator</code>包含任何形式的狀态,這些狀态必須是快照的一部分。<code>operator</code>狀态有不同的形式:

使用者定義的狀态:這種類型的狀态通過<code>transformation</code>函數(比如<code>map()</code>或者<code>filter()</code>)直接建立和修改。使用者定義的狀态可以是一個簡單的變量或者跟某個函數關聯的key/value狀态。

系統狀态:這種狀态通常關系到資料緩沖區,它們是<code>operator</code>計算邏輯的一部分。這種狀态的一個典型的例子是<code>window buffers</code>,在它内部,系統為其收集(以及聚合)記錄直到視窗被計算。

<code>operator</code>在從它們的所有輸入流中收到所有的快照屏障時,在發射屏障到它們的輸出流之前會對狀态做快照。在那個點,所有在屏障之前的記錄的狀态更新必須完成,并且在屏障之後依賴于記錄的更新不會被接收。因為快照的狀态有可能會非常大,它們被存儲在可配置的狀态終端上。預設存儲的位置是<code>JobManager</code>的記憶體,但為了嚴謹,應該配置一個分布式的可靠的存儲層(比如HDFS)。在狀态被存儲之後,<code>operator</code>會應答檢查點,發射快照屏障到輸出流并繼續處理流程。

現在快照的結果包含:

對每個并行流的資料源而言,快照開始時的偏移量或者位置

對每個<code>operator</code>而言,一個指針指向存儲在快照中的狀态部分

對齊步驟可能會增加流處理的延遲。通常這個額外的延遲被控制在毫秒級,但我們也看到一些場景下,延遲顯著增加。對于那些要求針對所有記錄的處理始終保持低延遲的應用(比如幾毫秒),Flink提供了一個開關(選項)可以在檢查點中跳過流對齊。檢查點快照仍然被建構,一旦<code>operator</code>從每個輸入流收到檢查點屏障。

當對齊操作被跳過,<code>operator</code>持續處理所有的輸入,甚至在檢查點n的一些檢查點屏障到達之後。這種情況下,<code>operator</code>在對檢查點n進行狀态快照之前也可能同時會處理屬于檢查點n+1的元素。是以,在恢複時,這些記錄可能會導緻重複,因為它們可能會既包含在針對檢查點n的快照中,又将包含在檢查點n之後被重放的部分資料中。

注意:對齊僅僅發生在<code>operator</code>有多個前置<code>operator</code>(join)以及<code>operator</code>有多個發送者(在一個流被<code>repartitioning/shuffle</code>之後)。正因為如此,令人尴尬的是,在資料流中僅僅隻有一個并行的流操作(<code>map()</code>,<code>flatMap()</code>,<code>filter()</code>…)時,即便在至少一次的模式下也能提供恰巧一次的一緻性保證。

在這個機制下的恢複是很簡單的:如果産生了失敗,Flink選擇最近完成的檢查點K。然後系統重放整個分布式的資料流,然後給予每個<code>operator</code>他們在檢查點k快照中的狀态。資料源被設定為從位置Sk開始重新讀取流。例如在Apache Kafka中,那意味着告訴消費者從偏移量Sk開始重新消費。

如果狀态被增量地快照,<code>operator</code>從最新的完整快照中讀取狀态然後在狀态上應用一系列的增量快照更新。

原文釋出時間為:2016-05-22

本文來自雲栖社群合作夥伴CSDN部落格,了解相關資訊可以關注CSDN部落格。