Apache 流框架 Flink,Spark Streaming,Storm對比分析(1)

此文已由做者嶽猛受權網易雲社區發佈。
html

歡迎訪問網易雲社區,瞭解更多網易技術產品運營經驗。算法

1.Flink架構及特性分析

Flink是個至關早的項目,開始於2008年,但只在最近才獲得注意。Flink是原生的流處理系統,提供high level的API。Flink也提供API來像Spark同樣進行批處理,但二者處理的基礎是徹底不一樣的。Flink把批處理看成流處理中的一種特殊狀況。在Flink中,全部的數據都看做流,是一種很好的抽象,由於這更接近於現實世界。緩存

1.1 基本架構

下面咱們介紹下Flink的基本架構,Flink系統的架構與Spark相似,是一個基於Master-Slave風格的架構。安全

當 Flink 集羣啓動後,首先會啓動一個 JobManger 和一個或多個的 TaskManager。由 Client 提交任務給 JobManager,JobManager 再調度任務到各個 TaskManager 去執行,而後 TaskManager 將心跳和統計信息彙報給 JobManager。TaskManager 之間以流的形式進行數據的傳輸。上述三者均爲獨立的 JVM 進程。網絡

Client 爲提交 Job 的客戶端,能夠是運行在任何機器上(與 JobManager 環境連通便可)。提交 Job 後,Client 能夠結束進程(Streaming的任務),也能夠不結束並等待結果返回。session

JobManager 主要負責調度 Job 並協調 Task 作 checkpoint,職責上很像 Storm 的 Nimbus。從 Client 處接收到 Job 和 JAR 包等資源後,會生成優化後的執行計劃,並以 Task 的單元調度到各個 TaskManager 去執行。多線程

TaskManager 在啓動的時候就設置好了槽位數(Slot),每一個 slot 能啓動一個 Task,Task 爲線程。從 JobManager 處接收須要部署的 Task,部署啓動後,與本身的上游創建 Netty 鏈接,接收數據並處理。架構

能夠看到 Flink 的任務調度是多線程模型,而且不一樣Job/Task混合在一個 TaskManager 進程中。主要組件爲JobManager,TaskManager,Client,下面簡要描述這三個組件的做用。app

JobManager

JobManager是Flink系統的協調者,它負責接收Flink Job,調度組成Job的多個Task的執行。同時,JobManager還負責收集Job的狀態信息,並管理Flink集羣中從節點TaskManager。JobManager所負責的各項管理功能,它接收到並處理的事件主要包括:框架

RegisterTaskManager

在Flink集羣啓動的時候,TaskManager會向JobManager註冊,若是註冊成功,則JobManager會向TaskManager回覆消息AcknowledgeRegistration。

SubmitJob

Flink程序內部經過Client向JobManager提交Flink Job,其中在消息SubmitJob中以JobGraph形式描述了Job的基本信息。

CancelJob

請求取消一個Flink Job的執行,CancelJob消息中包含了Job的ID,若是成功則返回消息CancellationSuccess,失敗則返回消息CancellationFailure。

UpdateTaskExecutionState

TaskManager會向JobManager請求更新ExecutionGraph中的ExecutionVertex的狀態信息,更新成功則返回true。

RequestNextInputSplit

運行在TaskManager上面的Task,請求獲取下一個要處理的輸入Split,成功則返回NextInputSplit。

JobStatusChanged

ExecutionGraph向JobManager發送該消息,用來表示Flink Job的狀態發生的變化,例如:RUNNING、CANCELING、FINISHED等。

TaskManager

TaskManager也是一個Actor,它是實際負責執行計算的Worker,在其上執行Flink Job的一組Task。每一個TaskManager負責管理其所在節點上的資源信息,如內存、磁盤、網絡,在啓動的時候將資源的狀態向JobManager彙報。TaskManager端能夠分紅兩個階段:

註冊階段

TaskManager會向JobManager註冊,發送RegisterTaskManager消息,等待JobManager返回AcknowledgeRegistration,而後TaskManager就能夠進行初始化過程。

可操做階段

該階段TaskManager能夠接收並處理與Task有關的消息,如SubmitTask、CancelTask、FailTask。若是TaskManager沒法鏈接到JobManager,這是TaskManager就失去了與JobManager的聯繫,會自動進入「註冊階段」,只有完成註冊才能繼續處理Task相關的消息。

Client

當用戶提交一個Flink程序時,會首先建立一個Client,該Client首先會對用戶提交的Flink程序進行預處理,並提交到Flink集羣中處理,因此Client須要從用戶提交的Flink程序配置中獲取JobManager的地址,並創建到JobManager的鏈接,將Flink Job提交給JobManager。Client會將用戶提交的Flink程序組裝一個JobGraph, 而且是以JobGraph的形式提交的。一個JobGraph是一個Flink Dataflow,它由多個JobVertex組成的DAG。其中,一個JobGraph包含了一個Flink程序的以下信息:JobID、Job名稱、配置信息、一組JobVertex等。


1.2 基於Yarn層面的架構

基於yarn層面的架構相似spark on yarn模式,都是由Client提交App到RM上面去運行,而後RM分配第一個container去運行AM,而後由AM去負責資源的監督和管理。須要說明的是,Flink的yarn模式更加相似spark on yarn的cluster模式,在cluster模式中,dirver將做爲AM中的一個線程去運行,在Flink on yarn模式也是會將JobManager啓動在container裏面,去作個driver相似的task調度和分配,YARN AM與Flink JobManager在同一個Container中,這樣AM能夠知道Flink JobManager的地址,從而AM能夠申請Container去啓動Flink TaskManager。待Flink成功運行在YARN集羣上,Flink YARN Client就能夠提交Flink Job到Flink JobManager,並進行後續的映射、調度和計算處理。

1.3 組件棧

Flink是一個分層架構的系統,每一層所包含的組件都提供了特定的抽象,用來服務於上層組件。

Deployment層

該層主要涉及了Flink的部署模式,Flink支持多種部署模式:本地、集羣(Standalone/YARN)、雲(GCE/EC2)。Standalone部署模式與Spark相似,這裏,咱們看一下Flink on YARN的部署模式

Runtime層

Runtime層提供了支持Flink計算的所有核心實現,好比:支持分佈式Stream處理、JobGraph到ExecutionGraph的映射、調度等等,爲上層API層提供基礎服務。

API層

API層主要實現了面向無界Stream的流處理和麪向Batch的批處理API,其中面向流處理對應DataStream API,面向批處理對應DataSet API。

Libraries層

該層也能夠稱爲Flink應用框架層,根據API層的劃分,在API層之上構建的知足特定應用的實現計算框架,也分別對應於面向流處理和麪向批處理兩類。面向流處理支持:CEP(復瑣事件處理)、基於SQL-like的操做(基於Table的關係操做);面向批處理支持:FlinkML(機器學習庫)、Gelly(圖處理)。


從官網中咱們能夠看到,對於Flink一個最重要的設計就是Batch和Streaming共同使用同一個處理引擎,批處理應用能夠以一種特殊的流處理應用高效地運行。

這裏面會有一個問題,就是Batch和Streaming是如何使用同一個處理引擎進行處理的。

1.4 Batch和Streaming是如何使用同一個處理引擎。

下面將從代碼的角度去解釋Batch和Streaming是如何使用同一處理引擎的。首先從Flink測試用例來區分二者的區別。


Batch WordCount Examples


Streaming WordCount Examples


Batch和Streaming採用的不一樣的ExecutionEnviroment,對於ExecutionEnviroment來講讀到的源數據是一個DataSet,而StreamExecutionEnviroment的源數據來講則是一個DataStream。

接着咱們追蹤下Batch的從Optimzer到JobGgraph的流程,這裏若是是Local模式構造的是LocalPlanExecutor,這裏咱們只介紹Remote模式,此處的executor爲RemotePlanExecutor


最終會調用ClusterClient的run方法將咱們的應用提交上去,run方法的第一步就是獲取jobGraph,這個是client端的操做,client會將jobGraph提交給JobManager轉化爲ExecutionGraph。Batch和streaming不一樣之處就是在獲取JobGraph上面。

若是咱們初始化的FlinkPlan是StreamingPlan,則首先構造Streaming的StreamingJobGraphGenerator去將optPlan轉爲JobGraph,Batch則直接採用另外一種的轉化方式。

簡而言之,Batch和streaming會有兩個不一樣的ExecutionEnvironment,不一樣的ExecutionEnvironment會將不一樣的API翻譯成不一樣的JobGgrah,JobGraph 之上除了 StreamGraph 還有 OptimizedPlan。OptimizedPlan 是由 Batch API 轉換而來的。StreamGraph 是由 Stream API 轉換而來的,JobGraph 的責任就是統一 Batch 和 Stream 的圖。

1.5 特性分析

高吞吐 & 低延遲


Flink 的流處理引擎只須要不多配置就能實現高吞吐率和低延遲。下圖展現了一個分佈式計數的任務的性能,包括了流數據 shuffle 過程。


支持 Event Time 和亂序事件

Flink 支持了流處理和 Event Time 語義的窗口機制。


Event time 使得計算亂序到達的事件或可能延遲到達的事件更加簡單。

狀態計算的 exactly-once 語義

流程序能夠在計算過程當中維護自定義狀態。


Flink 的 checkpointing 機制保證了即時在故障發生下也能保障狀態的 exactly once 語義。

高度靈活的流式窗口

Flink 支持在時間窗口,統計窗口,session 窗口,以及數據驅動的窗口


窗口能夠經過靈活的觸發條件來定製,以支持複雜的流計算模式。

帶反壓的連續流模型

數據流應用執行的是不間斷的(常駐)operators。


Flink streaming 在運行時有着自然的流控:慢的數據 sink 節點會反壓(backpressure)快的數據源(sources)。

容錯性

Flink 的容錯機制是基於 Chandy-Lamport distributed snapshots 來實現的。


這種機制是很是輕量級的,容許系統擁有高吞吐率的同時還能提供強一致性的保障。

Batch 和 Streaming 一個系統流處理和批處理共用一個引擎


Flink 爲流處理和批處理應用公用一個通用的引擎。批處理應用能夠以一種特殊的流處理應用高效地運行。

內存管理

Flink 在 JVM 中實現了本身的內存管理。


應用能夠超出主內存的大小限制,而且承受更少的垃圾收集的開銷。

迭代和增量迭代

Flink 具備迭代計算的專門支持(好比在機器學習和圖計算中)。


增量迭代能夠利用依賴計算來更快地收斂。

程序調優


批處理程序會自動地優化一些場景,好比避免一些昂貴的操做(如 shuffles 和 sorts),還有緩存一些中間數據。

API 和 類庫

流處理應用

DataStream API 支持了數據流上的函數式轉換,可使用自定義的狀態和靈活的窗口。

右側的示例展現瞭如何以滑動窗口的方式統計文本數據流中單詞出現的次數。

WindowWordCount in Flink's DataStream APIcase class Word(word: String, freq: Long)

 val texts: DataStream[String] = ... 

val counts = text .flatMap {

 line => line.split("\\W+") 

} .map { 

token => Word(token, 1) 

} .keyBy("word") .timeWindow(Time.seconds(5),

 Time.seconds(1)) .sum("freq"

批處理應用

Flink 的 DataSet API 可使你用 Java 或 Scala 寫出漂亮的、類型安全的、可維護的代碼。它支持普遍的數據類型,不只僅是 key/value 對,以及豐富的 operators。

右側的示例展現了圖計算中 PageRank 算法的一個核心循環。

case class Page(pageId: Long, rank: Double)case class Adjacency(id: Long, neighbors: Array[Long]) 

val result = initialRanks.iterate(30) { pages => pages.join(adjacency).where("pageId").equalTo("pageId") {

 (page, adj, out : Collector[Page]) => {

 out.collect(Page(page.id, 0.15 / numPages)) for (n <- adj.neighbors) {

 out.collect(Page(n, 0.85*page.rank/adj.neighbors.length)) 

 } 

 } 

 } .groupBy("pageId").sum("rank")}


類庫生態


Flink 棧中提供了提供了不少具備高級 API 和知足不一樣場景的類庫:機器學習、圖分析、關係式數據處理。當前類庫還在 beta 狀態,而且在大力發展。

普遍集成

Flink 與開源大數據處理生態系統中的許多項目都有集成。


Flink 能夠運行在 YARN 上,與 HDFS 協同工做,從 Kafka 中讀取流數據,能夠執行 Hadoop 程序代碼,能夠鏈接多種數據存儲系統。

部署性

Flink能夠單獨脫離Hadoop進行部署,部署只依賴Java環境,相對簡單。


免費體驗雲安全(易盾)內容安全、驗證碼等服務

更多網易技術、產品、運營經驗分享請點擊




相關文章:
【推薦】 appium封裝顯示等待Wait類和ExpectedCondition接口
【推薦】 移動端爬蟲工具與方法介紹
【推薦】 讓App飛久一點

相關文章
相關標籤/搜索