1 背景
我們在使用Flink開發實時任務時,都會用到框架本身提供的DataStream API,這使得使用者不能不用Java或者Scala甚至Python來編寫業務邏輯;這種方式雖然靈活且表達性強,但對使用者具有一定的開發門檻,並且隨著版本的不斷更新,DataStream API也有很多老版本不相容的問題。所以Flink SQL就成了廣大開發使用者的最佳選擇,之所以Flink推出SQL API,主要是因為SQL有如下幾個重要特性:
- 宣告式API:
使用者只關心做什麼,而不關心怎麼做; - 自動優化:
遮蔽底層API的複雜性,自動做優化; - 簡單易懂:
SQL應用於不同行業和領域,學習成本較低; - 不易變動:
語法遵循SQL標準規範,不易變動; - 流批統一:
同樣的SQL程式碼,可以用流和批的方式執行。
雖然Flink提供了SQL能力,但還是有必要基於Flink SQL打造屬於自己的平臺,目前搭建SQL平臺的方式有如下幾種:
- Flink原生API:
使用Flink提供的SQL API,封裝一個通用的pipeline jar,利用flink shell指令碼工具提交sql任務; - Apache Zeppelin:
一款開源產品,利用notebook方式管理sql任務,目前已經與Flink整合,且提供了豐富的SDK; - Flink Sql Gateway:
Flink官方出品的一個Sql閘道器,用Rest方式執行Flink Sql。
第一種方式缺乏靈活性,且大量提交任務時,有效能瓶頸;而Zeppelin雖然功能強大,但頁面功能有限,如果要基於Zeppelin打造SQL平臺,要麼使用SDK,要麼對Zeppelin做重度的二次開發;所以Flink Sql Gateway比較適合做平臺化建設,因為它是一個獨立的閘道器服務,方便與公司現有系統整合,完全與其它系統解耦,本文也主要闡述Flink Sql Gateway的實踐與探索。
2 Flink Sql Gateway 簡介
2.1 架構
如上圖所示,Flink Sql Gateway的架構比較簡單,主要元件是SqlGatewayEndpoint,它是基於Flink的RestServerEndpoint實現的一個Netty服務,通過自定義實現多種handler來完成sql任務的建立和部署,以及管理的能力。SqlGatewayEndpoint內部主要由SessionManager(會話管理)組成,SessionManager維護了一個session map,而session內部主要是一些上下文配置和環境資訊。
- SqlGatewayEndpoint:
基於RestServerEndpoint實現的Netty服務,對外提供Rest Api; - SessionManager :
會話管理器,管理session建立與刪除; - Session:
一個會話,裡面存放著任務所需要的Flink配置和上下文環境資訊,負責任務的執行; - Classpath:
Flink Sql Gateway啟動時會載入flink安裝目錄的classpath,所以flink sql gateway 基本上沒有除flink以外的相關依賴。
2.2 執行流程
sql gateway其實只是一個普通的NIO伺服器,每個Handler都會持有SessionManager的引用,因此可以共同訪問同一個SessionManager物件。當請求到達時,Handler會獲取請求中的引數,如SessionId等,去SessionManager中查詢對應的Session,從而執行提交sql、查詢任務狀態等工作。請求流程如下圖所示:
建立 session,這是使用sql gateway的第一步,SessionManager會把使用者傳入的任務執行模式、配置、planner引擎方式等引數封裝成Session物件,放入map中,並返回sessionid給使用者;
使用者持有sessionid,發起sql request的請求,gateway根據sessionid找到對應的Session物件,開始部署sql job到yarn / kubernetes;
2.3 功能
2.3.1 任務部署
Flink Sql Gateway作為Flink的客戶端,任務部署直接運用了Flink的能力,而 Flink目前支援三種部署模式:
- in Application Mode,
- in a Per-Job Mode,
- in Session Mode。
三種模式有如下兩個區別:
- 叢集生命週期和資源隔離:
per-job mode的叢集生命週期與job相同,但有較強的資源隔離保證。 - 應用程式的main()方法是在客戶端還是在叢集上執行:
session mode和per-job mode在客戶端上執行,而application mode在叢集上執行。
從以上可以看出,Application Mode為每個應用程式建立一個會話叢集,並在叢集上執行應用程式的 main() 方法,所以它是session mode和per-job的一個折中方案。
目前為止,Flink只支援jar包任務的application mode,所以想要實現sql任務的application mode,需要自己改造實現,後面會講實現方法。
2.3.2 SQL 能力
Flink Sql Gateway支援的Sql語法如下:
Flink Sql Gateway支援所有Flink Sql語法,但本身也有一些限制:
- 不支援多條sql執行,多條insert into執行會產生多個任務;
- 不完整的set支援,對於set語法支援存在bug;
- Sql Hit支援不是很友好,寫在sql裡比較容易出錯。
3 平臺化改造
3.1 SQL 的 application mode 實現
前面說到,flink不支援sql任務的application mode部署,只支援jar包任務。jar 包任務的application mode實現如下圖所示:
- flink-clients解析出使用者的配置和jar包資訊;
- ApplicationConfiguration裡指定了main方法的入口類名和入參;
- ApplicationDeployer負責把Jobmanager啟動,並且啟動時執行Flink Application的main方法。
通過以上流程可以看出,要實現sql的application mode,實現通用執行sql的pipeline jar是關鍵:
實現一個執行sql的通用pipeline jar包,並且預先傳到yarn或者k8s,如下所示:
在ApplicationConfiguration中指定
pepeline jar的main方法入口和引數:
3.2 多 Yarn 叢集支援
目前Flink只支援單Yarn環境的任務部署,對於擁有多套Yarn環境的場景,需要部署多套Flink環境,每個Flink對應一個Yarn環境配置;雖然這種方式能解決問題,但並不是最優的解決方案。熟悉Flink應該都知道,Flink使用 ClusterClientFactory的SPI來生成與外部資源系統(Yarn/kubernetes)的訪問介質(ClusterDescriptor),通過ClusterDescriptor可以完成與資源系統的互動,比如YarnClusterDescriptor,它持有YarnClient物件,可以完成與Yarn的互動;所以對於多Yarn環境,我們只要保證YarnClusterDescriptor 裡持有的YarnClient物件與Yarn環境一一對應即可,程式碼如下圖所示:
作者簡介
Zheng OPPO高階資料平臺工程師
主要負責基於Flink的實時計算平臺開發, 對Flink有較豐富的研發經驗, 也曾參與過Flink社群的貢獻。
獲取更多精彩內容,掃碼關注[OPPO數智技術]公眾號