關注公衆號:大數據技術派,回覆"資料",領取
1024G
資料。
這一課時我們將講解 Flink “精確一次”的語義實現原理,同時這也是面試的必考點。
Flink 的“精確一次”處理語義是,Flink 提供了一個強大的語義保證,也就是說在任何情況下都能保證數據對應用產生的效果只有一次,不會多也不會少。
那麼 Flink 是如何實現“端到端的精確一次處理”語義的呢?
背景
通常情況下,流式計算系統都會爲用戶提供指定數據處理的可靠模式功能,用來表明在實際生產運行中會對數據處理做哪些保障。一般來說,流處理引擎通常爲用戶的應用程序提供三種數據處理語義:最多一次、至少一次和精確一次。
最多一次(At-most-Once):這種語義理解起來很簡單,用戶的數據只會被處理一次,不管成功還是失敗,不會重試也不會重發。
至少一次(At-least-Once):這種語義下,系統會保證數據或事件至少被處理一次。如果中間發生錯誤或者丟失,那麼會從源頭重新發送一條然後進入處理系統,所以同一個事件或者消息會被處理多次。
精確一次(Exactly-Once):表示每一條數據只會被精確地處理一次,不多也不少。
Exactly-Once 是 Flink、Spark 等流處理系統的核心特性之一,這種語義會保證每一條消息只被流處理系統處理一次。“精確一次” 語義是 Flink 1.4.0 版本引入的一個重要特性,而且,Flink 號稱支持“端到端的精確一次”語義。
在這裏我們解釋一下“端到端(End to End)的精確一次”,它指的是 Flink 應用從 Source 端開始到 Sink 端結束,數據必須經過的起始點和結束點。Flink 自身是無法保證外部系統“精確一次”語義的,所以 Flink 若要實現所謂“端到端(End to End)的精確一次”的要求,那麼外部系統必須支持“精確一次”語義;然後藉助 Flink 提供的分佈式快照和兩階段提交才能實現。
分佈式快照機制
我們在之前的課程中講解過 Flink 的容錯機制,Flink 提供了失敗恢復的容錯機制,而這個容錯機制的核心就是持續創建分佈式數據流的快照來實現。
同 Spark 相比,Spark 僅僅是針對 Driver 的故障恢復 Checkpoint。而 Flink 的快照可以到算子級別,並且對全局數據也可以做快照。Flink 的分佈式快照受到 Chandy-Lamport 分佈式快照算法啓發,同時進行了量身定做,有興趣的同學可以搜一下。
Barrier
Flink 分佈式快照的核心元素之一是 Barrier(數據柵欄),我們也可以把 Barrier 簡單地理解成一個標記,該標記是嚴格有序的,並且隨着數據流往下流動。每個 Barrier 都帶有自己的 ID,Barrier 極其輕量,並不會干擾正常的數據處理。
如上圖所示,假如我們有一個從左向右流動的數據流,Flink 會依次生成 snapshot 1、 snapshot 2、snapshot 3……Flink 中有一個專門的“協調者”負責收集每個 snapshot 的位置信息,這個“協調者”也是高可用的。
Barrier 會隨着正常數據繼續往下流動,每當遇到一個算子,算子會插入一個標識,這個標識的插入時間是上游所有的輸入流都接收到 snapshot n。與此同時,當我們的 sink 算子接收到所有上游流發送的 Barrier 時,那麼就表明這一批數據處理完畢,Flink 會向“協調者”發送確認消息,表明當前的 snapshot n 完成了。當所有的 sink 算子都確認這批數據成功處理後,那麼本次的 snapshot 被標識爲完成。
這裏就會有一個問題,因爲 Flink 運行在分佈式環境中,一個 operator 的上游會有很多流,每個流的 barrier n 到達的時間不一致怎麼辦?這裏 Flink 採取的措施是:快流等慢流。
拿上圖的 barrier n 來說,其中一個流到的早,其他的流到的比較晚。當第一個 barrier n到來後,當前的 operator 會繼續等待其他流的 barrier n。直到所有的barrier n 到來後,operator 纔會把所有的數據向下發送。
異步和增量
按照上面我們介紹的機制,每次在把快照存儲到我們的狀態後端時,如果是同步進行就會阻塞正常任務,從而引入延遲。因此 Flink 在做快照存儲時,可採用異步方式。
此外,由於 checkpoint 是一個全局狀態,用戶保存的狀態可能非常大,多數達 G 或者 T 級別。在這種情況下,checkpoint 的創建會非常慢,而且執行時佔用的資源也比較多,因此 Flink 提出了增量快照的概念。也就是說,每次都是進行的全量 checkpoint,是基於上次進行更新的。
兩階段提交
上面我們講解了基於 checkpoint 的快照操作,快照機制能夠保證作業出現 fail-over 後可以從最新的快照進行恢復,即分佈式快照機制可以保證 Flink 系統內部的“精確一次”處理。但是我們在實際生產系統中,Flink 會對接各種各樣的外部系統,比如 Kafka、HDFS 等,一旦 Flink 作業出現失敗,作業會重新消費舊數據,這時候就會出現重新消費的情況,也就是重複消費。
針對這種情況,Flink 1.4 版本引入了一個很重要的功能:兩階段提交,也就是 TwoPhaseCommitSinkFunction。兩階段搭配特定的 source 和 sink(特別是 0.11 版本 Kafka)使得“精確一次處理語義”成爲可能。
在 Flink 中兩階段提交的實現方法被封裝到了 TwoPhaseCommitSinkFunction 這個抽象類中,我們只需要實現其中的beginTransaction、preCommit、commit、abort 四個方法就可以實現“精確一次”的處理語義,實現的方式我們可以在官網中查到:
- beginTransaction,在開啓事務之前,我們在目標文件系統的臨時目錄中創建一個臨時文件,後面在處理數據時將數據寫入此文件;
- preCommit,在預提交階段,刷寫(flush)文件,然後關閉文件,之後就不能寫入到文件了,我們還將爲屬於下一個檢查點的任何後續寫入啓動新事務;
- commit,在提交階段,我們將預提交的文件原子性移動到真正的目標目錄中,請注意,這會增加輸出數據可見性的延遲;
abort,在中止階段,我們刪除臨時文件。
Flink-Kafka Exactly-once
如上圖所示,我們用 Kafka-Flink-Kafka 這個案例來介紹一下實現“端到端精確一次”語義的過程,整個過程包括:
- 從 Kafka 讀取數據
- 窗口聚合操作
- 將數據寫回 Kafka
整個過程可以總結爲下面四個階段:
- 一旦 Flink 開始做 checkpoint 操作,那麼就會進入 pre-commit 階段,同時 Flink JobManager 會將檢查點 Barrier 注入數據流中 ;
- 當所有的 barrier 在算子中成功進行一遍傳遞,並完成快照後,則 pre-commit 階段完成;
- 等所有的算子完成“預提交”,就會發起一個“提交”動作,但是任何一個“預提交”失敗都會導致 Flink 回滾到最近的 checkpoint;
- pre-commit 完成,必須要確保 commit 也要成功,上圖中的 Sink Operators 和 Kafka Sink 會共同來保證。
現狀
目前 Flink 支持的精確一次 Source 列表如下表所示,你可以使用對應的 connector 來實現對應的語義要求:
數據源 | 語義保證 | 備註 |
---|---|---|
Apache Kafka | exactly once | 需要對應的 Kafka 版本 |
AWS Kinesis Streams | exactly once | |
RabbitMQ | at most once (v 0.10) / exactly once (v 1.0) | |
Twitter Streaming API | at most once | |
Collections | exactly once | |
Files | exactly once | |
Sockets | at most once |
如果你需要實現真正的“端到端精確一次語義”,則需要 sink 的配合。目前 Flink 支持的列表如下表所示:
寫入目標 | 語義保證 | 備註 |
---|---|---|
HDFS rolling sink | exactly once | 依賴 Hadoop 版本 |
Elasticsearch | at least once | |
Kafka producer | at least once / exactly once | 需要 Kafka 0.11 及以上 |
Cassandra sink | at least once / exactly once | 冪等更新 |
AWS Kinesis Streams | at least once | |
File sinks | at least once | |
Socket sinks | at least once | |
Standard output | at least once | |
Redis sink | at least once |
總結
由於強大的異步快照機制和兩階段提交,Flink 實現了“端到端的精確一次語義”,在特定的業務場景下十分重要,我們在進行業務開發需要語義保證時,要十分熟悉目前 Flink 支持的語義特性。
這一課時的內容較爲晦澀,建議你從源碼中去看一下具體的實現。
猜你喜歡
Spark SQL知識點與實戰
Hive計算最大連續登陸天數
Hadoop 數據遷移用法詳解
數倉建模分層理論
數倉建模—寬表的設計