取之開源,用之開源——深度剖析阿里巴巴對Apache Flink的優化與改進

Apache Flink 概述

Apache Flink(以下簡稱Flink)是誕生於歐洲的一個大數據研究項目,原名StratoSphere。該項目是柏林工業大學的一個研究性項目,早期專注於批計算。2014年,StratoSphere項目中的核心成員孵化出Flink,並在同年將Flink捐贈Apache,後來Flink順利成爲Apache的頂級大數據項目。同時Flink計算的主流方向被定位爲流計算,即用流式計算來做所有大數據的計算工作,這就是Flink技術誕生的背景。

2014年Flink作爲主攻流計算的大數據引擎開始在開源大數據行業內嶄露頭角。區別於Storm、Spark Streaming以及其他流式計算引擎的是:它不僅是一個高吞吐、低延遲的計算引擎,同時還提供很多高級功能。比如它提供有狀態的計算,支持狀態管理,支持強一致性的數據語義以及支持Event Time,WaterMark對消息亂序的處理等。

Flink的受歡迎還離不開它身上的衆多標籤,其中包括性能優秀(尤其在流計算領域)、高可擴展性、支持容錯,是一種純內存式的一個計算引擎,做了內存管理方面的大量優化,另外也支持eventime的處理、支持超大狀態的Job(在阿里巴巴中作業的state大小超過TB的是非常常見的)、支持exactly-once的處理。

阿里巴巴與Flink

隨着人工智能時代的降臨,數據量的爆發,在典型的大數據的業務場景下數據業務最通用的做法是:選用批處理的技術處理全量數據,採用流式計算處理實時增量數據。在絕大多數的業務場景之下,用戶的業務邏輯在批處理和流處理之中往往是相同的。但是,用戶用於批處理和流處理的兩套計算引擎是不同的。

因此,用戶通常需要寫兩套代碼。毫無疑問,這帶來了一些額外的負擔和成本。阿里巴巴的商品數據處理就經常需要面對增量和全量兩套不同的業務流程問題,所以阿里巴巴就在想:能不能有一套統一的大數據引擎技術,用戶只需要根據自己的業務邏輯開發一套代碼。這樣在各種不同的場景下,不管是全量數據還是增量數據,亦或者實時處理,一套方案即可全部支持,這就是阿里巴巴選擇Flink的背景和初衷。

基於Flink在阿里巴巴搭建的平臺於2016年正式上線,並從阿里巴巴的搜索和推薦這兩大場景開始實現。目前阿里巴巴所有的業務,包括阿里巴巴所有子公司都採用了基於Flink搭建的實時計算平臺。同時Flink計算平臺運行在開源的Hadoop集羣之上。採用Hadoop的YARN做爲資源管理調度,以 HDFS作爲數據存儲。因此,Flink可以和開源大數據軟件Hadoop無縫對接。

目前,這套基於Flink搭建的實時計算平臺不僅服務於阿里巴巴集團內部,而且通過阿里雲的雲產品API向整個開發者生態提供基於Flink的雲產品支持。

彼時的Flink不管是規模還是穩定性尚未經歷實踐,成熟度有待商榷。阿里巴巴實時計算團隊決定在阿里內部建立一個Flink分支Blink,並對Flink進行大量的修改和完善,讓其適應阿里巴巴這種超大規模的業務場景。在這個過程當中,該團隊不僅對Flink在性能和穩定性上做出了很多改進和優化,同時在覈心架構和功能上也進行了大量創新和改進,並將逐漸推回給社區,例如:Flink新的分佈式架構,增量Checkpoint機制,基於Credit-based的網絡流控機制和Streaming SQL等。接下來,我們主要從兩個層面深度剖析阿里巴巴對Flink究竟做了哪些優化?

取之開源,用之開源

一、 SQL層

爲了能夠真正做到用戶根據自己的業務邏輯開發一套代碼,能夠同時運行在多種不同的場景,Flink首先需要給用戶提供一個統一的API。在經過一番調研之後,阿里巴巴實時計算認爲SQL是一個非常適合的選擇。在批處理領域,SQL已經經歷了幾十年的考驗,是公認的經典。在流計算領域,近年來也不斷有流表二象性、流是表的ChangeLog等理論出現。在這些理論基礎之上,阿里巴巴提出了動態表的概念,使得流計算也可以像批處理一樣使用SQL來描述,並且邏輯等價。這樣一來,用戶就可以使用SQL來描述自己的業務邏輯,相同的查詢語句在執行時可以是一個批處理任務,也可以是一個高吞吐低延遲的流計算任務,甚至是先使用批處理技術進行歷史數據的計算,然後自動的轉成流計算任務處理最新的實時數據。在這種聲明式的API之下,引擎有了更多的選擇和優化空間。接下來,我們將介紹其中幾個比較重要的優化。

首先是對SQL層的技術架構進行升級和替換。調研過Flink或者使用過Flink的開發者應該知道,Flink有兩套基礎的API,一套是DataStream,另一套是DataSet。DataStream API是針對流式處理的用戶提供,DataSet API是針對批處理用戶提供,但是這兩套API的執行路徑是完全不一樣的,甚至需要生成不同的Task去執行。Flink原生的SQL層在經過一系列優化之後,會根據用戶希望是批處理還是流處理的不同選擇,去調用DataSet或者是DataStream API。這就會造成用戶在日常開發和優化中,經常要面臨兩套幾乎完全獨立的技術棧,很多事情可能需要重複的去做兩遍。這樣也會導致在一邊的技術棧上做的優化,另外一邊就享受不到。因此阿里巴巴在SQL層提出了全新的Quyer Processor,它主要包括一個流和批可以儘量做到複用的優化層(Query Optimizer)以及基於相同接口的算子層(Query Executor)。這樣一來, 80%以上的工作可以做到兩邊複用,比如一些公共的優化規則,基礎數據結構等等。同時,流和批也會各自保留自己一些獨特的優化和算子,以滿足不同的作業行爲。

在SQL層的技術架構統一之後,阿里巴巴開始尋求一種更高效的基礎數據結構,以便讓Blink在SQL層的執行更加高效。在原生Flink SQL中,都統一使用了一種叫Row的數據結構,它完全由JAVA的一些對象構成關係數據庫中的一行。假如現在的一行數據由一個整型,一個浮點型以及一個字符串組成,那麼Row當中就會包含一個JAVA的Integer、Double和String。衆所周知,這些JAVA的對象在堆內有不少的額外開銷,同時在訪問這些數據的過程中也會引入不必要的裝箱拆箱操作。基於這些問題,阿里巴巴提出了一種全新的數據結構BinaryRow,它和原來的Row一樣也是表示一個關係數據中的一行,但與之不同的是,它完全使用二進制數據來存儲這些數據。在上述例子中,三個不同類型的字段統一由JAVA的byte[]來表示。這會帶來諸多好處:

  • 首先在存儲空間上,去掉了很多無謂的額外消耗,使得對象的存儲更爲緊湊;
  • 其次在和網絡或者狀態存儲打交道的時候,也可以省略掉很多不必要的序列化反序列化開銷;
  • 最後在去掉各種不必要的裝箱拆箱操作之後,整個執行代碼對GC也更加友好。

通過引入這樣一個高效的基礎數據結構,整個SQL層的執行效率得到了一倍以上的提升。

在算子的實現層面,阿里巴巴引入了更廣範圍的代碼生成技術。得益於技術架構和基礎數據結構的統一,很多代碼生成技術得以達到更廣範圍的複用。同時由於SQL的強類型保證,用戶可以預先知道算子需要處理的數據的類型,從而可以生成更有針對性更高效的執行代碼。在原生Flink SQL中,只有類似a > 2或者c + d這樣的簡單表達式纔會應用代碼生成技術,在阿里巴巴優化之後,有一些算子會進行整體的代碼生成,比如排序、聚合等。這使得用戶可以更加靈活的去控制算子的邏輯,也可以直接將最終運行代碼嵌入到類當中,去掉了昂貴的函數調用開銷。一些應用代碼生成技術的基礎數據結構和算法,比如排序算法,基於二進制數據的HashMap等,也可以在流和批的算子之間進行共享和複用,讓用戶真正享受到了技術和架構的統一帶來的好處。在針對批處理的某些場景進行數據結構或者算法的優化之後,流計算的性能也能夠得到提升。接下來,我們聊聊阿里巴巴在Runtime層對Flink又大刀闊斧地進行了哪些改進。

二、 Runtime層

爲了讓Flink在Alibaba的大規模生產環境中生根發芽,實時計算團隊如期遇到了各種挑戰,首當其衝的就是如何讓Flink與其他集羣管理系統進行整合。Flink原生集羣管理模式尚未完善,也無法原生地使用其他其他相對成熟的集羣管理系統。基於此,一系列棘手的問題接連浮現:多租戶之間資源如何協調?如何動態的申請和釋放資源?如何指定不同資源類型?

爲了解決這個問題,實時計算團隊經歷大量的調研與分析,最終選擇的方案是改造Flink資源調度系統,讓Flink可以原生地跑在Yarn集羣之上;並且重構Master架構,讓一個Job對應一個Master,從此Master不再是集羣瓶頸。以此爲契機,阿里巴巴和社區聯手推出了全新的Flip-6架構,讓Flink資源管理變成可插拔的架構,爲Flink的可持續發展打下了堅實的基礎。如今Flink可以無縫運行在YARN、Mesos和K8s之上,正是這個架構重要性的有力說明。

解決了Flink集羣大規模部署問題後,接下來的就是可靠和穩定性,爲了保證Flink在生產環境中的高可用,阿里巴巴着重改善了Flink的FailOver機制。首先是Master的FailOver,Flink原生的Master FailOver會重啓所有的Job,改善後Master任何FailOver都不會影響Job的正常運行;其次引入了Region-based的Task FailOver,儘量減少任何Task的FailOver對用戶造成的影響。有了這些改進的保駕護航,阿里巴巴的大量業務方開始把實時計算遷移到Flink上運行。

Stateful Streaming是Flink的最大亮點,基於Chandy-Lamport算法的Checkpoint機制讓Flink具備Exactly Once一致性的計算能力,但在早期Flink版本中Checkpoint的性能在大規模數據量下存在一定瓶頸,阿里巴巴也在Checkpoint上進行了大量改進,比如:

  • 增量Checkpoint機制:阿里巴巴生產環境中遇到大JOB有幾十TB
    State是常事,做一次全量CP地動山搖,成本很高,因此阿里巴巴研發了增量Checkpoint機制,從此之後CP從暴風驟雨變成了細水長流;
  • Checkpoint小文件合併:都是規模惹的禍,隨着整個集羣Flink JOB越來越多,CP文件數也水漲船高,最後壓的HDFS NameNode不堪重負,阿里巴巴通過把若干CP小文件合併成一個大文件的組織方式,最終把NameNode的壓力減少了幾十倍。

雖然說所有的數據可以放在State中,但由於一些歷史的原因,用戶依然有一些數據需要存放在像HBase等一些外部KV存儲中,用戶在Flink Job需要訪問這些外部的數據,但是由於Flink一直都是單線程處理模型,導致訪問外部數據的延遲成爲整個系統的瓶頸,顯然異步訪問是解決這個問題的直接手段,但是讓用戶在UDF中寫多線程同時還要保證ExactlyOnce語義,卻並非易事。阿里巴巴在Flink中提出了AsyncOperator,讓用戶在Flink JOB中寫異步調用和寫“Hello Word”一樣簡單 ,這個讓Flink Job的吞吐有了很大的飛躍。

Flink在設計上是一套批流統一的計算引擎,在使用過快如閃電的流計算之後,批用戶也開始有興趣入住Flink小區。但批計算也帶來了新的挑戰,首先在任務調度方面,阿里巴巴引入了更加靈活的調度機制,能夠根據任務之間的依賴關係進行更加高效的調度;其次就是數據Shuffle,Flink原生的Shuffle Service和TM綁定,任務執行完之後要依舊保持TM無法釋放資源;還有就是原有的Batch shuffle沒有對文件進行合併,所以基本無法在生產中使用。阿里巴巴開發了Yarn Shuffle Service功能的同時解決了以上兩個問題。在開發Yarn Shuffle Service的時候,阿里巴巴發現開發一套新的Shuffle Service非常不便,需要侵入Flink代碼的很多地方,爲了讓其他開發者方便的擴展不同Shuffle,阿里巴巴同時改造了Flink Shuffle架構,讓Flink的Shuffle變成可插拔的架構。目前阿里巴巴的搜索業務已經在使用Flink Batch Job,並且已經開始服務於生產。

經過3年多打磨,Blink已經在阿里巴巴開始茁壯生長,但是對Runtime的優化和改進是永無止境的,一大波改進和優化正在路上。

Flink的未來方向

目前Flink已經是一個主流的流計算引擎,社區下一步很重要的工作是讓Flink在批計算上有所突破,在更多的場景下落地,成爲一種主流的批計算引擎。然後進一步在流和批之間進行無縫的切換,流和批的界限越來越模糊。用Flink,在一個計算中,既可以有流計算,又可以有批計算。

接下來阿里巴巴將致力於推動Flink在生態上得到更多語言的支持,不僅僅是Java、Scala語言,甚至是機器學習下用的Python、Go語言。

另一點不得不說AI,因爲現在很多大數據計算的需求和數據量都是在支持很火爆的AI場景,所以Flink在流批生態完善的基礎上,將繼續完善上層的Machine Learning算法庫,同時Flink也會向更成熟的機器學習、深度學習去集成。比如可以做Tensorflow On Flink, 讓大數據的ETL數據處理和機器學習的Feature計算和特徵計算,訓練的計算等進行集成,讓開發者能夠同時享受到多種生態給大家帶來的好處。

最後,從生態、社區的活躍來說,阿里巴巴目前在推進的一件事情是籌備2018年12月20日-21日在國家會議中心舉辦的首屆Flink Forward China峯會(千人規模),參與者將有機會了解阿里巴巴、騰訊、華爲、滴滴、美團、字節跳動等公司爲何將Flink作爲首選的流處理引擎。

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