流式計算(五)-Flink核心概念


一手資料,完全來自官網,直接參考英文過來的,並加了一些自己的理解,希望能讓看官君了解點什么,足矣。

環境:Flink1.9.1

難度:新手--戰士--老兵--大師

目標:

  1. 理解Flink的計算模型
  2. 認識各重要組件

說明:

本篇作為前兩篇的補充內容,算是理論篇

步驟:

01-Flink編程模型

Flink的流計算整體來看都是按照Source -> Transformation -> Sink三步走,即獲取流源 -> 進行轉換 -> 匯聚(Sink),但“轉換 (Transformation)”可能有多個步驟,比如依次進行 KeyBy、Reduce 和 Map 操作等,如下為示意圖:

 

02-並行數據流

Flink自誕生起即帶有並行與分布式特征,數據流在執行過程中,會被“分區(Partition)”,一般為按照Key進行邏輯分區,也可進行物理分區。每個算子(Operator)都有一到多個”算子子任務”,這些算子子任務彼此獨立,在不同的線程中執行,分布式環境下,可能在不同的服務器或容器(比如Docker)上執行。

每個算子子任務個數即為該算子的並行度(parallelism),可整體設置執行環境並行度從而作用於該環境下的所有算子,也可單獨的算子設置,這樣同一程序中不同的算子可以有不同的並行度。具體使用見我前面的文章篇二。並行度通常為生產型算子所具備,如下圖Map的並行度為2,Sink的為1:

 

03-算子間數據分發

“一對一(one-to-one)”:我們再看上圖,算子 Source 和 map() 之間就會保留元素的分區和順序,即map()子任務[1]接收的元素和Source子任務[1]產生的元素順序一致。

“再分配(Redistributing )”:上圖中的 map() 和 keyBy/window 之間,還有keyBy/window 和 Sink 之間,都會改變元素的分區。算子如 keyBy() / broadcast() / rebalance() 都可改變元素分區,結果就是只有上下游的算子子任務中相同分區內的元素順序不變,比如上圖中 map()的 subtask[1] 對接 keyBy/window 的 subtask[2],但是到了Sink算子,匯聚后的順序是不確定的,因為與各算子子任務的元素到達時間相關,比如上圖的的兩個算子子任務到Sink算子。

04-狀態計算

狀態概念見我前篇java steam中有解釋,可對比,所謂有狀態,就是程序需要記住上下文信息和一些中間計算結果,比如統計流元素個數,就必須有狀態。Flink中的狀態計算也非常普遍,有的是每次只關注於一個事件Event (事件即域模型的狀態改變的聲明,可以在流或批處理應用中做為輸入/輸出。是特殊類型的流元素。),比如event parser。有的是需要存儲多個事件的信息,比如Window算子。

狀態可視為通過內嵌的key/value存儲來維護。狀態會被分區並嚴格地分發給有狀態計算的流,因此狀態只能被已key分區( keyBy()之后)的流訪問,並且僅限於與當前事件的key有關聯的value。流之間keys和狀態的協調能保證所有的狀態更新都是本地的,從而確保不出現事務型開銷。這樣,Flink中狀態分發和分區調整都能透明的進行。如下圖中Source[1]和Source[2]中的相同的keys[DEZ],會保存在一個狀態中,並由Flink自動完成狀態的更新和協調。

 

05-檢查點(CheckPoint)容錯機制

Flink的容錯機制通過 stream replay 和 checkpointing組合來實現。檢查點就是流中一個特定的包含算子狀態的點。流可以從一個檢查點恢復,從而確保(exactly-once)語義。檢查點時間間隔大小可以調整容錯開銷和恢復時間兩者間的平衡。

06-批處理流:

Flink批處理模式下,即使用DataSet ,每次對一定數量的元素進行批處理,這與流處理基本一樣,但有些微區別:

  • 批處理模式下不使用檢查點(CheckPoint),因為元素個數是確定的,恢復時直接將相關流部分完全恢復,雖然恢復時負載大一些,但通常會使流處理更省資源。
  • DataSet API批處理時,使用簡化的in-memory/out-of-core數據結構保存狀態,而不是key/value存儲
  • DataSet API批處理獨有同步迭代

07-算子鏈(operator chain)

在分布式環境下,算子子任務(subtask)被分成多個任務組(tasks),每個任務組由一個線程執行。Flink進行了運行優化,也即對subtask進行鏈式操作,鏈式操作結束之后得到的task,再作為一個調度執行單元,放到一個線程里執行,這樣能減少線程間切換和緩存,好處當然是能提高吞吐量和降低延遲。如下圖,source/map 兩個算子進行了鏈式連接;keyby/window/apply進行了鏈式連接,sink單獨的一個。從而將有5個任務組,由5個線程並行執行。

 

08- JobManager和TaskManager

JobManager,也叫主(master)節點,負責協調分布式執行,具體如安排task,協調檢查點(checkpoint),協調故障恢復等。集群中至少有一個,高可用架構下會有多個,其中之一為事實上的領導者(leader),其他為候補(standby),leader發送故障時,standby轉為leader。

TaskManager ,也叫工人(worker)節點,負責執行數據流的子任務(task),還有緩存和數據交換。必須至少一個TaskManager。JobManager和TaskManager可以在獨立服務器、容器或者資源管理框架 YARN or Mesos中運行,然后TaskManager連接JobManager並聲明可用,進而被分配具體工作。

 

09-任務插槽(task slot)和資源

每個工人(TaskManager)就是一個JVM進程(process),能通過獨立線程執行一到多個子任務,子任務的可接收數量就叫任務插槽(task slot),至少得有1個。任務插槽代表TaskManager內的一組固定的資源集,一個TaskManager所有的任務插槽都會均分其控制的內存,比如有3個插槽的TaskManager ,各插槽被分配1/3其管理的內存,這樣的好處是為了避免子任務間的資源競爭。但目前這里不涉及CPU資源,僅是內存隔離。

通過調整任務插槽數,可以控制子任務的隔離度。比如TaskManager只有一個任務插槽,即意味着每個任務組運行在獨立的JVM(可運行在容器內)中,有多個任務插槽則意味着共享JVM內TCP連接(多路復用方式)和心跳消息,可能還共享數據集和數據結構,從而減少總體負載。

 

Flink默認對來自於不同Job的子任務都可以共享任務插槽,同一job的就更不用說了。這樣一個slot可能執行job的一個完整的管道流(從source到sink),好處有二。

  • Flink集群所需的任務插槽數與job中最高的並行度完全一致即可,並不需要去計算並行度各異的任務的總數。(這點的理解很重要,請看官君思考下)
  • 優化了資源利用。因為非密集型source/map()的任務組和密集型window 子任務占用同樣多的資源。下圖中,將前面的圖中的例子的並行度由2增加到6,這樣在有6個task slot的前提下,資源密集型source/map()任務就會被平均分配到各slot中,從而達到的slot的完全利用。

 

按照經驗,CPU核心數作為任務插槽數為最佳實踐。超線程場景下,每個slot會運行2到多個硬件線程上下文。

10-狀態后端(state backend)

使用key/values存儲狀態的具體數據結構,依賴於選擇的state backend 。一種是在內存hashMap存儲,另一種是在 RocksDB中存儲。狀態后端還將K/V狀態的時間點快照保存為檢查點(checkpoint)中的部分內容。

 

11-保存點(savepoint)

使用Data Stream API的程序能從保存點恢復執行(批處理的API無檢查點機制),不丟失狀態。

保存點實質上就是手動觸發的檢查點,會將程序快照寫入狀態后端。Flink的檢查點機制會周期性獲取woker上的快照並生成檢查點。如果為了恢復,只需要一個最新的已完成的檢查點,舊的檢查點就可以刪除了。savepoint和定期的checkpoint 是類似的,區別在於,它們是由用戶生成,且當新的checkpoint生成時,它不會自動過期。savepoint 可以通過命令行創建,也可以用REST API取消job時創建,區別總結如下:

  • Checkpoint 是增量式的,每次的時間較短,數據量較小,只要在程序里面啟用后會自動觸發,用戶無感知;Checkpoint 是作業 failover 的時候自動使用,不需要用戶指定。
  • Savepoint 是全量做的,每次的時間較長,數據量較大,需要用戶主動去觸發。Savepoint 一般用於程序的版本更新(詳見文檔),Bug 修復,A/B Test 等場景,需要用戶指定。

12-window生命周期

Window在屬於窗口的第一個元素到來時立即創建,然后在超過最后的時間戳加上設置的延遲后完全移除。Flink只能確定對基於時間特征的window做移除,其他類型的則需視具體環境而定。

每個window都帶有一個觸發器(Trigger)和一個處理函數。處理函數用於對window中的內容做計算,Trigger則是決定什么條件下函數開始處理。Trigger有每個元素觸發的,也有基於注冊的時間特征定時器(timer)到達時觸發的,還可以自定義。Trigger可以清空window的內容,但不是說刪除window元數據,從而window仍然可以接收新元素。

另外,每個window可指定驅逐器(Evictor ),用於在Trigger觸發后,也能在函數應用前/后移除一些元素,Evictor 可基於數量,時間戳或Delta算法等。比如只保留指定數量的元素,或移除時間戳超出最大指定值的元素。

此篇完!

總結:Flink內容還有很多,想要看得更全面,直接官網吧。

 

原創文章,禁止任何形式轉載,否則追究法律責任。

 

往期文章:

  1. 流式計算(四)-Flink Stream API 篇二
  2. 流式計算(三)-Flink Stream 篇一
  3. 流式計算(一)-Java8Stream
  4. Dubbo學習系列之十六(ELK海量日志分析)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM