【Flink】(01)Apache Flink 漫談系列 —— 概述

作者孫金城,花名 金竹,目前就職於阿里巴巴,自2015年以來一直投入於基於Apache Flink的阿里巴巴計算平臺Blink的設計研發工作。

一、Apache Flink 的命脈

“命脈” 即生命與血脈,常喻極爲重要的事物。系列的首篇,首篇的首段不聊Apache Flink的歷史,不聊Apache Flink的架構,不聊Apache Flink的功能特性,我們用一句話聊聊什麼是 Apache Flink 的命脈?我的答案是:Apache Flink 是以"批是流的特例"的認知進行系統設計的。

二、唯快不破

我們經常聽說 “天下武功,唯快不破”,大概意思是說 “任何一種武功的招數都是有拆招的,唯有速度快,快到對手根本來不及反應,你就將對手KO了,對手沒有機會拆招,所以唯快不破”。 那麼這與Apache Flink有什麼關係呢?Apache Flink是Native Streaming(純流式)計算引擎,在實時計算場景最關心的就是"快",也就是 “低延時”。

就目前最熱的兩種流計算引擎Apache Spark和Apache Flink而言,誰最終會成爲No1呢?單從 “低延時” 的角度看,Spark是Micro Batching(微批式)模式,延遲Spark能達到0.5~2秒左右,Flink是Native Streaming(純流式)模式,延時能達到微秒。很顯然是相對較晚出道的 Apache Flink 後來者居上。 那麼爲什麼Apache Flink能做到如此之 "快"呢?根本原因是Apache Flink 設計之初就認爲 “批是流的特例”,整個系統是Native Streaming設計,每來一條數據都能夠觸發計算。相對於需要靠時間來積攢數據Micro Batching模式來說,在架構上就已經佔據了絕對優勢。

那麼爲什麼關於流計算會有兩種計算模式呢?歸其根本是因爲對流計算的認知不同,是"流是批的特例" 和 “批是流的特例” 兩種不同認知產物。

1. Micro Batching 模式

Micro-Batching 計算模式認爲 “流是批的特例”, 流計算就是將連續不斷的批進行持續計算,如果批足夠小那麼就有足夠小的延時,在一定程度上滿足了99%的實時計算場景。那麼那1%爲啥做不到呢?這就是架構的魅力,在Micro-Batching模式的架構實現上就有一個自然流數據流入系統進行攢批的過程,這在一定程度上就增加了延時。具體如下示意圖:

很顯然Micro-Batching模式有其天生的低延時瓶頸,但任何事物的存在都有兩面性,在大數據計算的發展歷史上,最初Hadoop上的MapReduce就是優秀的批模式計算框架,Micro-Batching在設計和實現上可以借鑑很多成熟實踐。

2. Native Streaming 模式

Native Streaming 計算模式認爲 "“批是流的特例”,這個認知更貼切流的概念,比如一些監控類的消息流,數據庫操作的binlog,實時的支付交易信息等等自然流數據都是一條,一條的流入。Native Streaming 計算模式每條數據的到來都進行計算,這種計算模式顯得更自然,並且延時性能達到更低。具體如下示意圖:

很明顯Native Streaming模式佔據了流計算領域 “低延時” 的核心競爭力,當然Native Streaming模式的實現框架是一個歷史先河,第一個實現Native Streaming模式的流計算框架是第一個喫螃蟹的人,需要面臨更多的挑戰,後續章節我們會慢慢介紹。當然Native Streaming模式的框架實現上面很容易實現Micro-Batching和Batching模式的計算,Apache Flink就是Native Streaming計算模式的流批統一的計算引擎。

三、豐富的部署模式

Apache Flink 按不同的需求支持Local,Cluster,Cloud三種部署模式,同時Apache Flink在部署上能夠與其他成熟的生態產品進行集成,如 Cluster模式下可以利用YARN(Yet Another Resource Negotiator)/Mesos集成進行資源管理,在Cloud部署模式下可以與GCE(Google Compute Engine), EC2(Elastic Compute Cloud)進行集成。

1. Local 模式

該模式下Apache Flink 整體運行在Single JVM中,在開發學習中使用,同時也可以安裝到很多端類設備上。

2. Cluster模式

該模式是典型的投產的集羣模式,Apache Flink 既可以Standalone的方式進行部署,也可以與其他資源管理系統進行集成部署,比如與YARN進行集成。

這種部署模式是典型的Master/Slave模式,我們以Standalone Cluster模式爲例示意如下:

其中JM(JobManager)是Master,TM(TaskManager)是Slave,這種Master/Slave模式有一個典型的問題就是SPOF(single point of failure), SPOF如何解決呢?Apache Flink 又提供了HA(High Availability)方案,也就是提供多個Master,在任何時候總有一個JM服役,N(N>=1)個JM候選,進而解決SPOF問題,示意如下:

在實際的生產環境我們都會配置HA方案,目前Alibaba內部使用的也是基於YARN Cluster的HA方案。

3. Cloud 模式

該模式主要是與成熟的雲產品進行集成,Apache Flink官網介紹了Google的GCE 參考,Amazon的EC2 參考,在Alibaba我們也可以將Apache Flink部署到Alibaba的ECS(Elastic Compute Service)。

四、完善的容錯機制

1. 什麼是容錯

容錯(Fault Tolerance) 是指容忍故障,在故障發生時能夠自動檢測出來並使系統能夠自動回覆正常運行。當出現某些指定的網絡故障、硬件故障、軟件錯誤時,系統仍能執行規定的一組程序,或者說程序不會因系統中的故障而中止,並且執行結果也不會因系統故障而引起計算差錯。

2. 容錯的處理模式

在一個分佈式系統中由於單個進程或者節點宕機都有可能導致整個Job失敗,那麼容錯機制除了要保證在遇到非預期情況系統能夠"運行"外,還要求能"正確運行",也就是數據能按預期的處理方式進行處理,保證計算結果的正確性。計算結果的正確性取決於系統對每一條計算數據處理機制,一般有如下三種處理機制:

  • At Most Once:最多消費一次,這種處理機制會存在數據丟失的可能。
  • At Least Once:最少消費一次,這種處理機制數據不會丟失,但是有可能重複消費。
  • Exactly Once:精確一次,無論何種情況下,數據都只會消費一次,這種機制是對數據準確性的***要求,在金融支付,銀行賬務等領域必須採用這種模式。

3. Apache Flink的容錯機制

Apache Flink的Job會涉及到3個部分,外部數據源(External Input), Flink內部數據處理(Flink Data Flow)和外部輸出(External Output)。如下示意圖:

目前Apache Flink 支持兩種數據容錯機制:

  • At Least Once
  • Exactly Once

其中 Exactly Once 是最嚴格的容錯機制,該模式要求每條數據必須處理且僅處理一次。那麼對於這種嚴格容錯機制,一個完整的Flink Job容錯要做到 End-to-End 的 容錯必須結合三個部分進行聯合處理,根據上圖我們考慮三個場景:

  • 場景一:Flink的Source Operator 在讀取到Kafla中pos=2000的數據時候,由於某種原因宕機了,這個時候Flink框架會分配一個新的節點繼續讀取Kafla數據,那麼新的處理節點怎樣處理才能保證數據處理且只被處理一次呢?

  • 場景二:Flink Data Flow內部某個節點,如果上圖的agg()節點發生問題,在恢復之後怎樣處理才能保持map()流出的數據處理且只被處理一次?

  • 場景三:Flink的Sink Operator 在寫入Kafka過程中自身節點出現問題,在恢復之後如何處理,計算結果才能保證寫入且只被寫入一次?

4. 系統內部容錯

Apache Flink利用Checkpointing機制來處理容錯,Checkpointing的理論基礎 Stephan 在 Lightweight Asynchronous Snapshots for Distributed Dataflows 進行了細節描述,該機制源於有K. MANI CHANDY和LESLIE LAMPORT 發表的 Determining-Global-States-of-a-Distributed-System Paper。Apache Flink 基於Checkpointing機制對Flink Data Flow實現了At Least Once 和 Exactly Once 兩種容錯處理模式。

Apache Flink Checkpointing的內部實現會利用 Barriers,StateBackend等後續章節會詳細介紹的技術來將數據的處理進行Marker。Apache Flink會利用Barrier將整個流進行標記切分,如下示意圖:

這樣Apache Flink的每個Operator都會記錄當前成功處理的Checkpoint,如果發生錯誤,就會從上一個成功的Checkpoint開始繼續處理後續數據。比如 Soruce Operator會將讀取外部數據源的Position實時的記錄到Checkpoint中,失敗時候會從Checkpoint中讀取成功的position繼續精準的消費數據。每個算子會在Checkpoint中記錄自己恢復時候必須的數據,比如流的原始數據和中間計算結果等信息,在恢復的時候從Checkpoint中讀取並持續處理流數據。

5. 外部Source容錯

Apache Flink 要做到 End-to-End 的 Exactly Once 需要外部Source的支持,比如上面我們說過 Apache Flink的Checkpointing機制會在Source節點記錄讀取的Position,那就需要外部數據提供讀取的Position和支持根據Position進行數據讀取。

6. 外部Sink容錯

Apache Flink 要做到 End-to-End 的 Exactly Once 相對比較困難,如上場景三所述,當Sink Operator節點宕機,重新恢復時候根據Apache Flink 內部系統容錯 exactly once的保證,系統會回滾到上次成功的Checkpoin繼續寫入,但是上次成功Checkpoint之後當前Checkpoint未完成之前已經把一部分新數據寫入到kafka了. Apache Flink自上次成功的Checkpoint繼續寫入kafka,就造成了kafka再次接收到一份同樣的來自Sink Operator的數據,進而破壞了End-to-End 的 Exactly Once 語義(重複寫入就變成了At Least Once了),如果要解決這一問題,Apache Flink 利用Two phase commit(兩階段提交)的方式來進行處理。本質上是Sink Operator 需要感知整體Checkpoint的完成,並在整體Checkpoint完成時候將計算結果寫入Kafka。

五、流批統一的計算引擎

批與流是兩種不同的數據處理模式,如Apache Storm只支持流模式的數據處理,Apache Spark只支持批(Micro Batching)模式的數據處理。那麼Apache Flink 是如何做到既支持流處理模式也支持批處理模式呢?

1. 統一的數據傳輸層

開篇我們就介紹Apache Flink 的 "命脈"是以"批是流的特例"爲導向來進行引擎的設計的,系統設計成爲 "Native Streaming"的模式進行數據處理。那麼Apache FLink將批模式執行的任務看做是流式處理任務的特殊情況,只是在數據上批是有界的(有限數量的元素)。

Apache Flink 在網絡傳輸層面有兩種數據傳輸模式:

  • PIPELINED模式 - 即一條數據被處理完成以後,立刻傳輸到下一個節點進行處理。
  • BATCH 模式 - 即一條數據被處理完成後,並不會立刻傳輸到下一個節點進行處理,而是寫入到緩存區,如果緩存寫滿就持久化到本地硬盤上,***當所有數據都被處理完成後,纔將數據傳輸到下一個節點進行處理。

對於批任務而言同樣可以利用PIPELINED模式,比如我要做count統計,利用PIPELINED模式能拿到更好的執行性能。只有在特殊情況,比如SortMergeJoin,這時候我們需要全局數據排序,才需要BATCH模式。大部分情況流與批可用統一的傳輸策略,只有特殊情況,纔將批看做是流的一個特例繼續特殊處理。

2. 統一任務調度層

Apache Flink 在任務調度上流與批共享統一的資源和任務調度機制(後續章節會詳細介紹)。

3. 統一的用戶API層

Apache Flink 在DataStremAPI和DataSetAPI基礎上,爲用戶提供了流批統一的上層TableAPI和SQL,在語法和語義上流批進行高度統一。(其中DataStremAPI和DataSetAPI對流和批進行了分別抽象,這一點並不優雅,在Alibaba內部對其進行了統一抽象)。

4. 求同存異

Apache Flink 是流批統一的計算引擎,並不意味着流與批的任務都走統一的code path,在對底層的具體算子的實現也是有各自的處理的,在具體功能上面會根據不同的特性區別處理。比如 批沒有Checkpoint機制,流上不能做SortMergeJoin。

六、Apache Flink 架構

1. 組件棧

我們上面內容已經介紹了很多Apache Flink的各種組件,下面我們整體概覽一下全貌,如下:

TableAPI和SQL都建立在DataSetAPI和DataStreamAPI的基礎之上,那麼TableAPI和SQL是如何轉換爲DataStream和DataSet的呢?

2. TableAPI&SQL到DataStrem&DataSet的架構

TableAPI&SQL最終會經過Calcite優化之後轉換爲DataStream和DataSet,具體轉換示意如下:

對於流任務最終會轉換成DataStream,對於批任務最終會轉換成DataSet。

3. ANSI-SQL的支持

Apache Flink 之所以利用ANSI-SQL作爲用戶統一的開發語言,是因爲SQL有着非常明顯的優點,如下:

  • Declarative - 用戶只需要表達我想要什麼,不用關心如何計算。
  • Optimized - 查詢優化器可以爲用戶的 SQL 生成的執行計劃,獲取的查詢性能。
  • Understandable - SQL語言被不同領域的人所熟知,用SQL 作爲跨團隊的開發語言可以很大地提高效率。
  • Stable - SQL 是一個擁有幾十年歷史的語言,是一個非常穩定的語言,很少有變動。
    Unify - Apache Flink在引擎上對流與批進行統一,同時又利用ANSI-SQL在語法和語義層面進行統一。

4. 擴展的優化機制

Apache Flink 利用Apache Calcite對SQL進行解析和優化,Apache Calcite採用Calcite是開源的一套查詢引擎,實現了兩套Planner:

  • HepPlanner - 是RBO(Rule Base Optimize)模式,基於規則的優化。
  • VolcanoPlanner - 是CBO(Cost Base Optimize)模式,基於成本的優化。

Flink SQL會利用Calcite解析優化之後,最終轉換爲底層的DataStrem和Dataset。上圖中 Batch rules和Stream rules可以根據優化需要添加優化規則。

七、豐富的類庫和算子

Apache Flink 優秀的架構就像一座摩天大廈的地基一樣爲Apache Flink 持久的生命力打下了良好的基礎,爲打造Apache Flink豐富的功能生態留下的空間。

1. 類庫

  • CEP - 複雜事件處理類庫,核心是一個狀態機,廣泛應用於事件驅動的監控預警類業務場景。
  • ML - 機器學習類庫,機器學習主要是識別數據中的關係、趨勢和模式,一般應用在預測類業務場景。
  • GELLY - 圖計算類庫,圖計算更多的是考慮邊和點的概念,一般被用來解決網狀關係的業務場景。

2. 算子

Apache Flink 提供了豐富的功能算子,對於數據流的處理來講,可以分爲單流處理(一個數據源)和多流處理(多個數據源)。

3. 多流操作

UNION - 將多個字段類型一致數據流合併爲一個數據流,如下示意:

JOIN - 將多個數據流(數據類型可以不一致)聯接爲一個數據流,如下示意:

如上通過UION和JOIN我們可以將多流最終變成單流,Apache Flink 在單流上提供了更多的操作算子。

4. 單流操作

將多流變成單流之後,我們按數據輸入輸出的不同歸類如下:

如上表格對單流上面操作做簡單歸類,除此之外還可以做 過濾,排序,窗口等操作,我們後續章節會逐一介紹。

5. 存在的問題

Apache Flink 目前的架構還存在很大的優化空間,比如前面提到的DataStreamAPI和DataSetAPI其實是流與批在API層面不統一的體現,同時看具體實現會發現DataStreamAPI會生成Transformation tree然後生成StreamGraph,生成JobGraph,底層對應StreamTask,但DataSetAPI會形成Operator tree,flink-optimize模塊會對Batch Plan進行優化,形成Optimized Plan 後形成JobGraph,形成BatchTask。具體示意如下:

這種情況其實 DataStreamAPI到Runtime 和 DataSetAPI到Runtime的實現上並沒有得到程度的統一和複用。在這一點上面Aalibab 對Apache Flink 的增強在架構和實現上都進行了進一步優化。

八、Alibaba對Apache Flink的增強架構

1. 組件棧

Alibaba 對Apache Flink進行了大量的架構優化,如下架構是一直努力的方向,大部分功能還在持續開發中,具體如下:

如上架構我們發現較大的變化是:

Query Processing - 我們增加了Query Processing層,在這一層進行統一的流和批的查詢優化和底層算子的轉換。
DAG API - 我們在Runtime層面統一抽象API接口,在API層對流與批進行統一。

2. TableAPI&SQL到Runtime的架構

Apache Flink執行層是流批統一的設計,在API和算子設計上面我們儘量達到流批的共享,在TableAPI和SQL層無論是流任務還是批任務最終都轉換爲統一的底層實現。示意圖如下:

這個層面最核心的變化是批最終也會生成StreamGraph,執行層運行Stream Task。

九、特別說明

後續章節會以Alibaba對Apache Flink的增強爲主介紹功能算子,篇章中分享的功能可能開源暫時沒有,但這些的內容後續Alibaba會共享給社區,需要大家耐心等待。

十、小結

本篇概要的介紹了"批是流的特例"這一設計觀點是Apache Flink的"命脈",它決定了Apache Flink的運行模式是純流式的,這在實時計算場景的"低延遲"需求上,相對於Micro Batching模式佔據了架構的絕對優勢,同時概要的向大家介紹了Apache Flink的部署模式,容錯處理,引擎的統一性和Apache Flink的架構,和大家分享了Alibaba對Apache Flink的增強架構,以及對開源Apache Flink所作出的優化。

本篇沒有對具體技術進行詳細展開,大家只要對Apache Flink有初步感知,頭腦中知道Alibaba對Apache Flink進行了架構優化,增加了衆多功能就可以了,至於Apache Flink的具體技術細節和實現原理,以及Alibaba對Apache Flink做了哪些架構優化和增加了哪些功能後續章節會展開介紹!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章