Client
當(dāng)用戶提交一個Flink程序時,會首先創(chuàng)建一個Client,該Client首先會對用戶提交的Flink程序進(jìn)行預(yù)處理,并提交到Flink集群中處理,所以Client需要從用戶提交的Flink程序配置中獲取JobManager的地址,并建立到JobManager的連接,將Flink Job提交給JobManager。Client會將用戶提交的Flink程序組裝一個JobGraph, 并且是以JobGraph的形式提交的。一個JobGraph是一個Flink Dataflow,它由多個JobVertex組成的DAG。其中,一個JobGraph包含了一個Flink程序的如下信息:JobID、Job名稱、配置信息、一組JobVertex等。
組件棧
Flink是一個分層架構(gòu)的系統(tǒng),每一層所包含的組件都提供了特定的抽象,用來服務(wù)于上層組件。Flink分層的組件棧如下圖所示:

flink-component-stack
下面,我們自下而上,分別針對每一層進(jìn)行解釋說明:
Deployment層
該層主要涉及了Flink的部署模式,F(xiàn)link支持多種部署模式:本地、集群(Standalone/YARN)、云(GCE/EC2)。Standalone部署模式與Spark類似,這里,我們看一下Flink alt="物聯(lián)網(wǎng)" width="550" height="253" />
flink-on-yarn
了解YARN的話,對上圖的原理非常熟悉,實際Flink也實現(xiàn)了滿足在YARN集群上運(yùn)行的各個組件:Flink YARN Client負(fù)責(zé)與YARN RM通信協(xié)商資源請求,F(xiàn)link JobManager和Flink TaskManager分別申請到Container去運(yùn)行各自的進(jìn)程。通過上圖可以看到,YARN AM與Flink JobManager在同一個Container中,這樣AM可以知道Flink JobManager的地址,從而AM可以申請Container去啟動Flink TaskManager。待Flink成功運(yùn)行在YARN集群上,F(xiàn)link YARN Client就可以提交Flink Job到Flink JobManager,并進(jìn)行后續(xù)的映射、調(diào)度和計算處理。
Runtime層
Runtime層提供了支持Flink計算的全部核心實現(xiàn),比如:支持分布式Stream處理、JobGraph到ExecutionGraph的映射、調(diào)度等等,為上層API層提供基礎(chǔ)服務(wù)。
API層
API層主要實現(xiàn)了面向無界Stream的流處理和面向Batch的批處理API,其中面向流處理對應(yīng)DataStream API,面向批處理對應(yīng)DataSet API。
Libraries層
該層也可以稱為Flink應(yīng)用框架層,根據(jù)API層的劃分,在API層之上構(gòu)建的滿足特定應(yīng)用的實現(xiàn)計算框架,也分別對應(yīng)于面向流處理和面向批處理兩類。面向流處理支持:CEP(復(fù)雜事件處理)、基于SQL-like的操作(基于Table的關(guān)系操作);面向批處理支持:FlinkML(機(jī)器學(xué)習(xí)庫)、Gelly(圖處理)。
內(nèi)部原理
容錯機(jī)制
Flink基于Checkpoint機(jī)制實現(xiàn)容錯,它的原理是不斷地生成分布式Streaming數(shù)據(jù)流Snapshot。在流處理失敗時,通過這些Snapshot可以恢復(fù)數(shù)據(jù)流處理。理解Flink的容錯機(jī)制,首先需要了解一下Barrier這個概念:
Stream Barrier是Flink分布式Snapshotting中的核心元素,它會作為數(shù)據(jù)流的記錄被同等看待,被插入到數(shù)據(jù)流中,將數(shù)據(jù)流中記錄的進(jìn)行分組,并沿著數(shù)據(jù)流的方向向前推進(jìn)。每個Barrier會攜帶一個Snapshot ID,屬于該Snapshot的記錄會被推向該Barrier的前方。因為Barrier非常輕量,所以并不會中斷數(shù)據(jù)流。帶有Barrier的數(shù)據(jù)流,如下圖所示:

flink-stream-barriers
基于上圖,我們通過如下要點來說明:
出現(xiàn)一個Barrier,在該Barrier之前出現(xiàn)的記錄都屬于該Barrier對應(yīng)的Snapshot,在該Barrier之后出現(xiàn)的記錄屬于下一個Snapshot
來自不同Snapshot多個Barrier可能同時出現(xiàn)在數(shù)據(jù)流中,也就是說同一個時刻可能并發(fā)生成多個Snapshot
當(dāng)一個中間(Intermediate)Operator接收到一個Barrier后,它會發(fā)送Barrier到屬于該Barrier的Snapshot的數(shù)據(jù)流中,等到Sink Operator接收到該Barrier后會向Checkpoint Coordinator確認(rèn)該Snapshot,直到所有的Sink Operator都確認(rèn)了該Snapshot,才被認(rèn)為完成了該Snapshot
這里還需要強(qiáng)調(diào)的是,Snapshot并不僅僅是對數(shù)據(jù)流做了一個狀態(tài)的Checkpoint,它也包含了一個Operator內(nèi)部所持有的狀態(tài),這樣才能夠在保證在流處理系統(tǒng)失敗時能夠正確地恢復(fù)數(shù)據(jù)流處理。也就是說,如果一個Operator包含任何形式的狀態(tài),這種狀態(tài)必須是Snapshot的一部分。
Operator的狀態(tài)包含兩種:一種是系統(tǒng)狀態(tài),一個Operator進(jìn)行計算處理的時候需要對數(shù)據(jù)進(jìn)行緩沖,所以數(shù)據(jù)緩沖區(qū)的狀態(tài)是與Operator相關(guān)聯(lián)的,以窗口操作的緩沖區(qū)為例,F(xiàn)link系統(tǒng)會收集或聚合記錄數(shù)據(jù)并放到緩沖區(qū)中,直到該緩沖區(qū)中的數(shù)據(jù)被處理完成;另一種是用戶自定義狀態(tài)(狀態(tài)可以通過轉(zhuǎn)換函數(shù)進(jìn)行創(chuàng)建和修改),它可以是函數(shù)中的Java對象這樣的簡單變量,也可以是與函數(shù)相關(guān)的Key/Value狀態(tài)。