Flink核心概念:系統架構、時間處理、狀態與檢查點
- 2019 年 12 月 25 日
- 筆記
本文是Flink學習筆記系列的第四篇文章,主要分享Flink系統架構、時間處理、狀態與檢查點等核心概念,包括API抽象、JobManager與TaskManager、Flink作業運行基本流程、時間戳與Watermark、狀態後端等。
系統架構
Flink API 抽象
Flink給編程人員提供了不同層次的API抽象。

Flink API抽象結構 來源:Flink官網
- Flink最底層提供的是有狀態的流式計算引擎,流(Stream)、狀態(State)和時間(Time)等流式計算概念都在這一層得到了實現。
- 一般情況下,應用程式不會使用上述底層介面,而是使用Flink提供的核心API:針對有界和無界數據流的DataStream API和針對有界數據集的DataSet API。用戶可以使用這兩個API進行常用的數據處理:轉換(Transformation)、連接(Join)、聚合(Aggregation)、窗口(Window)以及狀態(State)。這一層有點像Spark提供的RDD級別的介面。
- Table API和SQL是更高級別的抽象。在這一層,數據被轉換成了關係型資料庫式的表格,每個表格擁有一個表模式(Schema),用戶可以像操作表格那樣操作流式數據,例如可以使用針對結構化數據的
select
、join
、group-by
等操作。如果用戶熟悉SQL語句、pandas的DataFrame或者Spark的DataFrame,那麼可以很快上手Flink的Table API和SQL。很多公司的數據流非常依賴SQL,Flink SQL降低了從其他框架遷移至Flink的成本。
Flink數據流圖
之前的文章曾提到了流式計算引擎邏輯視角與物理視角。

Flink示常式序與對應邏輯視角 來源:Flink官網
上圖的Flink示常式序對一個數據流做簡單處理,整個過程包括了輸入(Source)、轉換(Transformation)和輸出(Sink)。程式由多個DataStream API組成,這些API,又被稱為運算元 (Operator),共同組成了邏輯視角。在實際執行過程中,邏輯視角會被計算引擎翻譯成可並行的物理視角。

並行物理視角
在實際執行過程中,這些API或者說這些運算元是並行地執行的。在大數據領域,當數據量大到超過單台機器處理能力時,就將一份數據切分到多個分區(pattition)上,每個分區分布在一個虛擬機或物理機。從物理視角上看,每個運算元是並行的,一個運算元有一個或多個運算元子任務(Subtask),每個運算元子任務只處理一小部分數據,所有運算元子任務共同組成了一個運算元。根據運算元所做的任務不同,運算元子任務的個數可能也不同。上圖的例子中,map、keyBy等運算元下面的[1]和[2]表示運算元子任務分別運行在第一和第二個分區上,子任務個數都是2;只有數據輸出的Sink運算元個數是1。運算元子任務是相互獨立的,一個運算元子任務有自己的執行緒,不同運算元子任務可能分布在不同的物理機或虛擬機上。
從上圖可以看到,運算元子任務之間要做數據交換,數據交換主要包括:
- 前向:例如source[1]的輸出可以直接傳遞給map[1],不需要跨節點,數據仍然在同一個分區內,以同樣的順序被處理。
- 數據重分配:子任務將數據發送給不同節點上的其他子任務,例如map[1]的結果會被keyBy運算元重分配到[1]或者[2]兩個節點上,數據的分布會因此發生變化。
Flink核心組件
為了實現支援上述並行物理視角,Flink跟其他大數據系統一樣,採用了主從(master-worker)架構,運行時主要包括兩個進程:
- JobManager,又被稱為master,是一個Flink應用的主節點。
- TaskManager,又被稱為worker,執行計算任務的節點。
如下圖所示,一個Flink應用一般含有至少一個JobManager,一個或多個TaskManager。

Flink架構與作業提交流程
用戶編寫Flink應用並提交任務的具體流程為:
- 用戶在客戶端(Client)編寫應用程式程式碼。程式一般為Java或Scala語言,調用Flink API運算元,構建基於邏輯視角的數據流圖。程式碼和相關配置文件被編譯打包,被提交到JobManager上,形成一個應用作業(Application)。
- JobManager接受到作業後,將邏輯視角的數據流圖轉化成可並行執行的物理視角數據流圖。
- JobManager將物理視角數據流圖發送給各TaskManager。
- TaskManager執行被分配的任務。
- TaskManager在執行任務過程中可能會與其他TaskManager交換數據。
- TaskManager中的任務啟動、運行、性能指標、結束或終止等狀態資訊會回饋給JobManager。
- 用戶可以使用Flink Web儀錶盤來監控提交的作業。
資源與資源隔離
在電腦領域,計算資源一般指CPU、記憶體、網路和存儲資源。基於現代虛擬化技術,我們可以將一台物理機上的計算資源虛擬化成多個虛擬機。本節簡單介紹Flink的資源隔離的機制,並不關注資源虛擬化和調度,這些是資源調度器YARN或Mesos所關注的事情。
上一節提到,TaskManager是直接執行具體任務的基本單位,一個TaskManager中的任務可以是某一個運算元的子任務,也可以是不同運算元的子任務。TaskManager提供一些槽位(Slot),計算任務被分配到這些槽位中執行。

運算元、任務與槽位示意圖
上圖展示了運算元、任務以及槽位之間的關係:左側為一個含有5個運算元的邏輯視角數據流圖,右側為在TaskManager上執行的並行物理視角。Flink給這個作業分配2個TaskManager,每個TaskManager有2個槽位,共4個計算槽位。每個槽位都包含A、B、C、D運算元子任務。A、B子任務在交換數據時不需要跨槽位,這將降低數據傳輸資源開銷,C、D子任務之間會跨槽位,產生一些數據傳輸開銷。
在實現TaskManager過程中,Flink在一個Java進程(Process)中啟動多個執行緒(Thread)來並行執行這些任務。比起進程,執行緒的優勢在於更輕量化、數據傳輸開銷更小;執行緒的劣勢是隔離性差,某一個任務出現錯誤可能導致整個TaskManager上的所有計算都崩潰。不過,Flink高度兼容不同的資源調度框架,如YARN、Mesos或Kubernetes,因此,為了有效隔離計算任務,可以給一個Flink任務單獨創建一個Flink集群,或者在分配資源時將某台物理機上的所有資源都分配給同一個TaskManager,這樣即使該應用出現問題,也不會影響其他應用。
時間處理
在上一章中,我們提到流式大數據處理引擎對時間的複雜要求,並解釋了Event Time與Processing Time的區別。Event Time是某個數據實際發生的時間,Processing Time是流式系統處理該條數據的時間。從實際發生到系統接收中間這個過程有一些不確定的延遲,使用Processing Time作為時間,會產生不可復現的結果;使用Event Time作為時間,可以得到一致的、可復現的結果。Event Time雖然準確,但也有其弊端:流式系統無法預知某個時間下,是否所有數據均已到達,因此需要使用Watermark機制處理延遲數據。
Flink應用中每個數據記錄包含一個時間戳,時間戳的定義跟業務場景有關,但是一般使用事件實際發生的時間,即Event Time。時間戳一般基於Unix時間戳,即以1970-01-01-00:00:00.000為起始點。毫秒精度是事件距離該起點的毫秒總數,微秒精度是事件距離該起點的微秒總數。
Watermark
在上一章我們已經提到,Watermark機制假設在某個時間點上,不會有比這個時間點更晚的上報數據。Watermark常被作為一個時間窗口的結束時間。

一個帶有watermark的數據流
Flink中的Watermark是被系統插入到數據流的特殊數據。Watermark的時間戳單調遞增,且與事件時間戳相關。如上圖的數據流所示,方塊是事件,三角形是該事件對應的時間戳,圓圈為Watermark。當Flink接受到時間戳值為5的Watermark時,系統假設時間戳小於5的事件均已到達,後續到達的小於5的事件均為延遲數據。Flink處理到最新的Watermark,會開啟這個時間窗口的計算,把這個Watermark之前的數據納入進此次計算,延遲數據則不能被納入進來,因此這種計算時有一定微小誤差的。
生成Watermark
流數據中的事件時間戳與Watermark高度相關,事件時間戳的抽取和Watermark的生成也基本是同時進行的,抽取的過程會遇到下面兩種情況:
- 數據流中已經包含了事件時間戳和Watermark。
- 使用抽取運算元生成事件時間戳和Watermark,這也是實際應用中更為常見的場景。因為後續的計算都依賴時間,抽取運算元最好在數據接入後馬上使用。具體而言,抽取運算元包含兩個函數:第一個函數從數據流的事件中抽取時間戳,並將時間戳賦值到事件的元數據上,第二個函數生成Watermark。
Flink有兩種方式來生成Watermark:
- 周期性(Periodic)生成Watermark:Flink每隔一定時間間隔,定期調用Watermark生成函數。這種方式下,Watermark的生成與時間有周期性的關係。
- 打點式(Punctuated)生成Watermark:數據流中某些帶有特殊標記的數據自帶了Watermark資訊,Flink監控數據流中的每個事件,當接收到帶有特殊標記數據時,會觸發Watermark的生成。這種方式下,Watermark的生成與時間無關,與何時接收到特殊標記數據有關。
無論是以上那種方式,Flink都會生成Watermark並插入到數據流中。一旦時間戳和Watermark生成後,後續的運算元將以Event Time的時間語義來處理這個數據流。Flink把時間處理部分的程式碼都做了封裝,會在內部處理各類時間問題,用戶不需要擔心延遲數據等任何時間相關問題。因此,Flink用戶只需要在數據接入的一開始生成時間戳和Watermark,Flink會負責剩下的事情。
延遲數據
Flink有一些機制專門收集和處理延遲數據。遲到事件在Watermark之後到達,一般處理的方式有三種:
- 將遲到事件作為錯誤事件直接丟棄。
- 將遲到事件收集起來另外再處理。
- 重新觸發計算。
對於第二種方式,用戶可以使用Flink提供的Side Output
機制,將遲到事件放入一個單獨的數據流,以便再對其單獨處理。
對於第三種方式,用戶可以使用Flink提供的Allowed Lateness
機制,設置一個允許的最大遲到時長,原定的時間窗口關閉後,Flink仍然會保存該窗口的狀態,直至超過遲到時長,遲到的事件加上原來的事件一起重新被計算。
狀態與檢查點
狀態
在上一章中我們已經提到了狀態的概念,流式大數據處理引擎會根據流入數據持續更新狀態數據。狀態可以是當前所處理事件的位置偏移(Offset)、一個時間窗口內的某種輸入數據、或與具體作業有關的自定義變數。

數據流與狀態示意圖
如上圖所示的應用,我們計算一個實時數據流的最大值與最小值,這個作業的狀態包括當前處理的位置偏移、已處理過的最大值和最小值等變數資訊。
Checkpoint
由於分散式大數據系統運行在多台機器上,因此經常會遇到某台機器宕機、網路出現延遲抖動等問題,一旦出現宕機等問題,該機器上的狀態以及相應的計算會丟失,因此需要一種恢復機制來應對這些潛在問題。
Flink使用檢查點(Checkpoint)技術來做失敗恢復。檢查點一般是將狀態數據生成快照(Snapshot),持久化存儲起來,一旦發生意外,Flink主動重啟應用,並從最近的快照中恢復,再繼續處理新流入數據。
Flink採用的是一種一致性檢查點(Consistent Checkpoint)技術,它可以將分布在多台機器上的所有狀態都記錄下來,並提供了Exactly-Once的投遞保障,其背後是使用了Chandy-Lamport演算法,將本地的狀態數據存儲到一個存儲空間上,並在故障恢復時在多台機器上恢復當前狀態。
狀態後端
Flink提供了3種存儲狀態的方式:
- 記憶體
- 文件系統
- RocksDB
這三種存儲方式又被稱為狀態後端(State Backend)。
記憶體
使用這種方式,Flink會將狀態維護在Java堆上。眾所周知,記憶體的訪問讀寫速度最快;其缺點也顯而易見,單台機器的記憶體空間有限,不適合存儲大數據量的狀態資訊。一般在本地開發調試時或者狀態非常小的應用場景下使用記憶體這種方式。
如不做特殊配置,Flink默認使用記憶體作為Backend。
文件系統
文件系統包括:
- 本地文件系統
- 分散式文件系統,如HDFS、S3。
當選擇使用文件系統作為後端時,正在計算的數據會被暫存在TaskManager的記憶體中。Checkpoint時,此後端會將狀態快照寫入配置的文件系統中,同時會在JobManager的記憶體中或者在 Zookeeper 中(高可用情況)存儲極少的元數據。
文件系統後端適用於處理大狀態,長窗口,或大鍵值狀態的任務。
RocksDB
RocksDB是一種嵌入式鍵值資料庫,由Facebook開發。使用RocksDB作為後端時,Flink會將實時處理中的數據使用RocksDB存儲在本地磁碟上。Checkpoint時,整個RocksDB資料庫會被存儲到配置的文件系統中,同時Flink會將極少的元數據存儲在JobManager的記憶體中,或者在Zookeeper中(高可用情況)。
RocksDB支援增量Checkpoint,即只對修改的數據做備份,因此非常適合超大狀態的場景。
Savepoint
在容錯上,除了Checkpoint,Flink還提供了Savepoint機制。從名稱和實現上,這兩個機制都極其相似,甚至Savepoint機制會使用Checkpoint機制的數據,但實際上,這兩個機制的定位不同。

Checkpoint 與 Savepoint
Checkpoint是Flink定時觸發並自動執行的容錯恢復機制,以應對各種意外情況;Savepoint是一種特殊的Checkpoint,它需要編程人員手動介入。比如,用戶更新某個應用的程式碼,需要先停掉該應用並重啟,這時就需要使用Savepoint。
小結
本文簡述了Flink的一些核心概念,包括系統架構、時間處理、狀態與檢查點。用戶可以通過本文了解Flink的基本運行方式。