分佈式技術的發展,深入地改變了咱們編程的模式和思考軟件的模式。值 2019 歲末,PingCAP 聯合 InfoQ 共同策劃出品「分佈式系統前沿技術 」專題, 邀請衆多技術團隊共同參與,一塊兒探索這個古老領域的新生機。本文出自微衆銀行大數據平臺負責人邸帥。
在當前的複雜分佈式架構環境下,服務治理已經大行其道。但目光往下一層,從上層 APP、Service,到底層計算引擎這一層面,卻仍是各個引擎各自爲政,Client-Server 模式緊耦合滿天飛的狀況。如何作好「計算治理」,讓複雜環境下各類類型的大量計算任務,都能更簡潔、靈活、有序、可控的提交執行,和保障成功返回結果?計算中間件 Linkis 就是上述問題的最佳實踐。前端
分佈式架構,指的是系統的組件分佈在經過網絡相連的不一樣計算機上,組件之間經過網絡傳遞消息進行通訊和協調,協同完成某一目標。通常來講有水平(集羣化)和垂直(功能模塊切分)兩個拆分方向,以解決高內聚低耦合、高併發、高可用等方面問題。git
多個分佈式架構的系統,組成分佈式系統羣,就造成了一個相對複雜的分佈式架構環境。一般包含多種上層應用服務,多種底層基礎計算存儲引擎。以下圖 1 所示:github
<center>圖 1</center>編程
就像《微服務設計》一書中提到的,如同城市規劃師在面對一座龐大、複雜且不斷變化的城市時,所須要作的規劃、設計和治理同樣,龐大複雜的軟件系統環境中的各類區域、元素、角色和關係,也須要整治和管理,以使其以一種更簡潔、優雅、有序、可控的方式協同運做,而不是變成一團亂麻。後端
在當前的複雜分佈式架構環境下,大量 APP、Service 間的通訊、協調和管理,已經有了從 SOA(Service-Oriented Architecture)到微服務的成熟理念,及從 ESB 到 Service Mesh 的衆多實踐,來實現其從服務註冊發現、配置管理、網關路由,到流控熔斷、日誌監控等一系列完整的服務治理功能。服務治理框架的「中間件」層設計,能夠很好的實現服務間的解耦、異構屏蔽和互操做,並提供路由、流控、狀態管理、監控等治理特性的共性提煉和複用,加強整個架構的靈活性、管控能力、可擴展性和可維護性。設計模式
但目光往下一層,你會發如今從 APP、Service,到後臺引擎這一層面,卻仍是各個引擎各自爲政,Client-Server 模式緊耦合滿天飛的狀況。在大量的上層應用,和大量的底層引擎之間,缺少一層通用的「中間件」框架設計。相似下圖 2 的網狀。緩存
<center>圖 2</center>安全
計算治理,關注的正是上層應用和底層計算(存儲)引擎之間,從 Client 到 Server 的鏈接層範圍,所存在的緊耦合、靈活性和管控能力欠缺、缺少複用能力、可擴展性、可維護性差等問題。要讓複雜分佈式架構環境下各類類型的計算任務,都能更簡潔、靈活、有序、可控的提交執行,和成功返回結果。以下圖 3 所示:網絡
<center>圖 3</center>多線程
更詳細的來看計算治理的問題,能夠分爲以下治(architecture,架構層面)和理(insight,細化特性)兩個層面。
緊耦合問題,上層應用和底層計算存儲引擎間的 CS 鏈接模式。
全部 APP & Service 和底層計算存儲引擎,都是經過 Client-Server 模式相連,處於緊耦合狀態。以 Analytics Engine 的 Spark 爲例,以下圖 4:
<center>圖 4</center>
這種狀態會帶來以下問題:
重複造輪子問題,每一個上層應用工具系統都要重複解決計算治理問題。
每一個上層應用都要重複的去集成各類 client,建立和管理 client 到引擎的鏈接及其狀態,包括底層引擎元數據的獲取與管理。在併發使用的用戶逐漸變多、併發計算任務量逐漸變大時,每一個上層應用還要重複的去解決多個用戶間在 client 端的資源爭用、權限隔離,計算任務的超時管理、失敗重試等等計算治理問題。
<center>圖 5</center>
想象你有 10 個併發任務數過百的上層應用,不論是基於 Web 的 IDE 開發環境、可視化 BI 系統,仍是報表系統、工做流調度系統等,每一個接入 3 個底層計算引擎。上述的計算治理問題,你可能得逐一重複的去解決 10*3=30 遍,而這正是當前在各個公司不斷髮生的現實狀況,其形成的人力浪費不可小覷。
擴展難問題,上層應用新增對接底層計算引擎,維護成本高,改動大。
在 CS 的緊耦合模式下,上層應用每新增對接一個底層計算引擎,都須要有較大改動。
以對接 Spark 爲例,在上層應用系統中的每一臺須要提交 Spark 做業的機器,都須要部署和維護好 Java 和 Scala 運行時環境和變量,下載和部署 Spark Client 包,且配置並維護 Spark 相關的環境變量。若是要使用 Spark on YARN 模式,那麼你還須要在每一臺須要提交 Spark 做業的機器上,去部署和維護 Hadoop 相關的 jar 包和環境變量。再若是你的 Hadoop 集羣須要啓用 Kerberos 的,那麼很不幸,你還須要在上述的每臺機器去維護和調試 keytab、principal 等一堆 Kerberos 相關配置。
<center>圖 6</center>
這還僅僅是對接 Spark 一個底層引擎。隨着上層應用系統和底層引擎的數量增多,須要維護的關係會是個笛卡爾積式的增加,光 Client 和配置的部署維護,就會成爲一件很使人頭疼的事情。
應用孤島問題,跨不一樣應用工具、不一樣計算任務間的互通問題。
多個相互有關聯的上層應用,向後臺引擎提交執行的不一樣計算任務之間,每每是有所關聯和共性的,好比須要共享一些用戶定義的運行時環境變量、函數、程序包、數據文件等。當前狀況每每是一個個應用系統就像一座座孤島,相關信息和資源沒法直接共享,須要手動在不一樣應用系統裏重複定義和維護。
典型例子是在數據批處理程序開發過程當中,用戶在數據探索開發 IDE 系統中定義的一系列變量、函數,到了數據可視化系統裏每每又要從新定義一遍;IDE 系統運行生成的數據文件位置和名稱,不能直接方便的傳遞給可視化系統;依賴的程序包也須要從 IDE 系統下載、從新上傳到可視化系統;到了工做流調度系統,這個過程還要再重複一遍。不一樣上層應用間,計算任務的運行依賴缺少互通、複用能力。
<center>圖 7</center>
除了上述的架構層面問題,要想讓複雜分佈式架構環境下,各類類型的計算任務,都能更簡潔、靈活、有序、可控的提交執行,和成功返回結果,計算治理還需關注高併發,高可用,多租戶隔離,資源管控,安全加強,計算策略等等細化特性問題。這些問題都比較直白易懂,這裏就不一一展開論述了。
計算中間件 Linkis,是微衆銀行專門設計用來解決上述緊耦合、重複造輪子、擴展難、應用孤島等計算治理問題的。當前主要解決的是複雜分佈式架構的典型場景-數據平臺環境下的計算治理問題。
Linkis 做爲計算中間件,在上層應用和底層引擎之間,構建了一層中間層。可以幫助上層應用,經過其對外提供的標準化接口(如 HTTP, JDBC, Java …),快速的鏈接到多種底層計算存儲引擎(如 Spark、Hive、TiSpark、MySQL、Python 等),提交執行各類類型的計算任務,並實現跨上層應用間的計算任務運行時上下文和依賴的互通和共享。且經過提供多租戶、高併發、任務分發和管理策略、資源管控等特性支持,使得各類計算任務更靈活、可靠、可控的提交執行,成功返回結果,大大下降了上層應用在計算治理層的開發和運維成本、與整個環境的架構複雜度,填補了通用計算治理軟件的空白。(圖 八、9)
<center>圖 8</center>
<center>圖 9</center>
要更詳細的瞭解計算任務經過 Linkis 的提交執行過程,咱們先來看看 Linkis 核心的「計算治理服務」部分的內部架構和流程。以下圖 10:
<center>圖 10</center>
計算治理服務:計算中間件的核心計算框架,主要負責做業調度和生命週期管理、計算資源管理,以及引擎鏈接器的生命週期管理。
公共加強服務:通用公共服務,提供基礎公共功能,可服務於 Linkis 各類服務及上層應用系統。
其中計算治理服務的主要模塊以下:
如圖 10 所示,一個做業的提交執行主要分爲如下 11 步:
1. 上層應用向計算中間件提交做業,微服務網關 SpringCloud Gateway 接收做業並轉發給 Entrance。
2. Entrance 消費做業,爲做業向 AppManager 申請可用 EngineConn。
3. 若是不存在可複用的 Engine,AppManager 嘗試向 ResourceManager 申請資源,爲做業啓動一個新 EngineConn。
5. EngineConnManager 啓動新 EngineConn,並主動回推新 EngineConn 信息。
6. AppManager 將新 EngineConn 分配給 Entrance,Entrance 將 EngineConn 分配給用戶做業,做業開始執行,將計算任務提交給 EngineConn。
7. EngineConn 將計算任務提交給底層計算引擎。
8. EngineConn 實時監聽底層引擎執行狀況,回推相關日誌、進度和狀態給 Entrance,Entrance 經過 WebSocket,主動回推 EngineConn 傳過來的日誌、進度和狀態給上層應用系統。
9. EngineConn 執行完成後,回推計算任務的狀態和結果集信息,Entrance 將做業和結果集信息更新到 JobHistory,並通知上層應用系統。
10. 上層應用系統訪問 JobHistory,拿到做業和結果集信息。
11. 上層應用系統訪問 Storage,請求做業結果集。
在複雜分佈式環境下,一個計算任務每每不單會是簡單的提交執行和返回結果,還可能須要面對提交失敗、執行失敗、hang 住等問題,且在大量併發場景下還需經過計算任務的調度分發,解決租戶間互相影響、負載均衡等問題。
Linkis 經過對計算任務的標籤化,實現了在任務調度、分發、路由等方面計算任務管理策略的支持,並可按需配置超時、自動重試,及灰度、多活等策略支持。以下圖 11。
<center>圖 11</center>
說完了業務架構,咱們如今來聊聊技術架構。在計算治理層環境下,不少類型的計算任務具備生命週期較短的特徵,如一個 Spark job 可能幾十秒到幾分鐘就執行完,EngineConn(EnginConnector)會是大量動態啓停的狀態。前端用戶和 Linkis 中其餘管理角色的服務,須要可以及時動態發現相關服務實例的狀態變化,並獲取最新的服務實例訪問地址信息。同時須要考慮,各模塊間的通訊、路由、協調,及各模塊的橫向擴展、負載均衡、高可用等能力。
基於以上需求,Linkis 實際是基於 Spring Cloud 微服務框架技術,將上述的每個模塊/角色,都封裝成了一個微服務,構建了多個微服務組,整合造成了 Linkis 的完整計算中間件能力。以下圖 12:
<center>圖 12</center>
從多租戶管理角度,上述服務可區分爲租戶相關服務,和租戶無關服務兩種類型。租戶相關服務,是指一些任務邏輯處理負荷重、資源消耗高,或須要根據具體租戶、用戶、物理機器等,作隔離劃分、避免相互影響的服務,如 Entrance、 EnginConn(EnginConnector) Manager、EnginConn;其餘如 App Manger、Resource Manager、Context Service 等服務,都是租戶無關的。
Eureka 承擔了微服務動態註冊與發現中心,及全部租戶無關服務的負載均衡、故障轉移功能。
Eureka 有個侷限,就是在其客戶端,對後端微服務實例的發現與狀態刷新機制,是客戶端主動輪詢刷新,最快可設 1 秒 1 次(實際要幾秒才能完成刷新)。這樣在 Linkis 這種須要快速刷新大量後端 EnginConn 等服務的狀態的場景下,時效得不到知足,且定時輪詢刷新對 Eureka server、對後端微服務實例的成本都很高。
爲此咱們對 Spring Cloud Ribbon 作了改造,在其中封裝了 Eureka client 的微服務實例狀態刷新方法,並把它作成知足條件主動請求刷新,而不會再頻繁的按期輪詢。從而在知足時效的同時,大大下降了狀態獲取的成本。以下圖 13:
<center>圖 13</center>
Spring Cloud Gateway 承擔了外部請求 Linkis 的入口網關的角色,幫助在服務實例不斷髮生變化的狀況下,簡化前端用戶的調用邏輯,快速方便的獲取最新的服務實例訪問地址信息。
Spring Cloud Gateway 有個侷限,就是一個 WebSocket 客戶端只能將請求轉發給一個特定的後臺服務,沒法完成一個 WebSocket 客戶端經過網關 API 對接後臺多個 WebSocket 微服務,而這在咱們的 Entrance HA 等場景須要用到。
爲此 Linkis 對 Spring Cloud Gateway 作了相應改造,在 Gateway 中實現了 WebSocket 路由轉發器,用於與客戶端創建 WebSocket 鏈接。創建鏈接成功後,會自動分析客戶端的 WebSocket 請求,經過規則判斷出請求該轉發給哪一個後端微服務,而後將 WebSocket 請求轉發給對應的後端微服務實例。詳見 Github 上 Linkis 的 Wiki 中,「Gateway 的多 WebSocket 請求轉發實現」一文。
<center>圖 14</center>
Spring Cloud OpenFeign 提供的 HTTP 請求調用接口化、解析模板化能力,幫助 Linkis 構建了底層 RPC 通訊框架。但基於 Feign 的微服務之間 HTTP 接口的調用,只能知足簡單的 A 微服務實例根據簡單的規則隨機選擇 B 微服務之中的某個服務實例,而這個 B 微服務實例若是想異步回傳信息給調用方,是沒法實現的。同時,因爲 Feign 只支持簡單的服務選取規則,沒法作到將請求轉發給指定的微服務實例,沒法作到將一個請求廣播給接收方微服務的全部實例。Linkis 基於 Feign 實現了一套本身的底層 RPC 通訊方案,集成到了全部 Linkis 的微服務之中。一個微服務既能夠做爲請求調用方,也能夠做爲請求接收方。做爲請求調用方時,將經過 Sender 請求目標接收方微服務的 Receiver;做爲請求接收方時,將提供 Receiver 用來處理請求接收方 Sender 發送過來的請求,以便完成同步響應或異步響應。以下圖示意。詳見 GitHub 上 Linkis 的 Wiki 中,「Linkis RPC 架構介紹」一文。
<center>圖 15</center>
至此,Linkis 對上層應用和底層引擎的解耦原理,其核心架構與流程設計,及基於 Spring Cloud 微服務框架實現的,各模塊微服務化動態管理、通訊路由、橫向擴展能力介紹完畢。
Linkis 做爲計算中間件,在上層應用和底層引擎之間,構建了一層中間層。上層應用全部計算任務,先經過 HTTP、WebSocket、Java 等接口方式提交給 Linkis,再由 Linkis 轉交給底層引擎。原有的上層應用以 CS 模式直連底層引擎的緊耦合得以解除,所以實現瞭解耦。以下圖 16 所示:
<center>圖 16</center>
經過解耦,底層引擎的變更有了 Linkis 這層中間件緩衝,如引擎 client 的版本升級,無需再對每個對接的上層應用作逐個改動,可在 Linkis 層統一完成。並能在 Linkis 層,實現對上層應用更加透明和友好的升級策略,如灰度切換、多活等策略支持。且即便後繼接入更多上層應用和底層引擎,整個環境複雜度也不會有大的變化,大大下降了開發運維工做負擔。
有了 Linkis,上層應用能夠基於 Linkis,快速實現對多種後臺計算存儲引擎的對接支持,及變量、函數等自定義與管理、資源管控、多租戶、智能診斷等計算治理特性。
以微衆銀行與 Linkis 同時開源的,交互式數據開發探索工具 Scriptis 爲例,Scriptis 的開發人員只需關注 Web UI、多種數據開發語言支持、腳本編輯功能等純前端功能實現,Linkis 包辦了其從存儲讀寫、計算任務提交執行、做業狀態日誌更新、資源管控等等幾乎全部後臺功能。基於 Linkis 的大量計算治理層能力的複用,大大下降了 Scriptis 項目的開發成本,使得 Scritpis 目前只須要有限的前端人員,便可完成維護和版本迭代工做。
以下圖 17,Scriptis 項目 99.5% 的代碼,都是前端的 JS、CSS 代碼。後臺基本徹底複用 Linkis。
<center>圖 17</center>
模塊化可插拔的計算引擎接入設計,新引擎接入簡單快速。
對於典型交互式模式計算引擎(提交任務,執行,返回結果),用戶只須要 buildApplication 和 executeLine 這 2 個方法,就能夠完成一個新的計算引擎接入 Linkis,代碼量極少。示例以下。
(1) AppManager 部分:用戶必須實現的接口是 ApplicationBuilder,用來封裝新引擎鏈接器實例啓動命令。
1. //用戶必須實現的方法: 用於封裝新引擎鏈接器實例啓動命令 2. def buildApplication(protocol:Protocol):ApplicationRequest
(2) EngineConn部分:用戶只需實現executeLine方法,向新引擎提交執行計算任務:
1. //用戶必須實現的方法:用於調用底層引擎提交執行計算任務 2. def executeLine(context: EngineConnContext,code: String): ExecuteResponse
引擎相關其餘功能/方法都已有默認實現,無定製化需求可直接複用。
經過 Linkis 提供的上下文服務,和存儲、物料庫服務,接入的多個上層應用之間,可輕鬆實現環境變量、函數、程序包、數據文件等,相關信息和資源的共享和複用,打通應用孤島。
<center>圖 18</center>
Context Service(CS)爲不一樣上層應用系統,不一樣計算任務,提供了統一的上下文管理服務,可實現上下文的自定義和共享。在 Linkis 中,CS 須要管理的上下文內容,可分爲元數據上下文、數據上下文和資源上下文 3 部分。
<center>圖 19</center>
元數據上下文,定義了計算任務中底層引擎元數據的訪問和使用規範,主要功能以下:
Linkis 計算治理細化特性設計與實現介紹,在高併發、高可用、多租戶隔離、資源管控、計算任務管理策略等方面,作了大量細化考量和實現,保障計算任務在複雜條件下成功執行。
Linkis 的 Job 基於多級異步設計模式,服務間經過高效的 RPC 和消息隊列模式進行快速通訊,並能夠經過給 Job 打上建立者、用戶等多種類型的標籤進行任務的轉發和隔離來提升 Job 的併發能力。經過 Linkis 能夠作到 1 個入口服務(Entrance)同時承接超 1 萬+ 在線的 Job 請求。
多級異步的設計架構圖以下:
<center>圖 20</center>
如上圖所示 Job 從 GateWay 到 Entrance 後,Job 從生成到執行,到信息推送經歷了多個線程池,每一個環節都經過異步的設計模式,每個線程池中的線程都採用運行一次即結束的方式,下降線程開銷。整個 Job 從請求—執行—到信息推送全都異步完成,顯著的提升了 Job 的併發能力。
這裏針對計算任務最關鍵的一環 Job 調度層進行說明,海量用戶成千上萬的併發任務的壓力,在 Job 調度層中是如何進行實現的呢?
在請求接收層,請求接收隊列中,會緩存前端用戶提交過來的成千上萬計算任務,並按系統/用戶層級劃分的調度組,分發到下游 Job 調度池中的各個調度隊列;到 Job 調度層,多個調度組對應的調度器,會同時消費對應的調度隊列,獲取 Job 並提交給 Job 執行池進行執行。過程當中大量使用了多線程、多級異步調度執行等技術。示意以下圖 21:
<center>圖 21</center>
Linkis 還在高可用、多租戶隔離、資源管控、計算任務管理策略等方面,作了不少細化考量和實現。篇幅有限,在這裏再也不詳述每一個細化特性的實現,可參見 Github 上 Linkis 的 Wiki。後繼咱們會針對 Linkis 的計算治理-理之路(Insight)的細化特性相關內容,再作專題介紹。
基於如上解耦、複用、快速擴展、連通等架構設計優勢,及高併發、高可用、多租戶隔離、資源管控等細化特性實現,計算中間件 Linkis 在微衆生產環境的應用效果顯著。極大的助力了微衆銀行一站式大數據平臺套件 WeDataSphere 的快速構建,且構成了 WeDataSphere 全連通、多租戶、資源管控等企業級特性的基石。
Linkis 在微衆應用狀況如圖 22:
<center>圖 22</center>
咱們已將 Linkis 開源,Github repo 地址:https://github.com/WeBankFinTech/Linkis。
歡迎對相似計算治理問題感興趣的同窗,參與到計算中間件 Linkis 的社區協做中,共同把 Linkis 建設得更加完善和易用。
做者介紹:邸帥,微衆銀行大數據平臺負責人,主導微衆銀行 WeDataSphere 大數據平臺套件的建設運營與開源,具有豐富的大數據平臺開發建設實踐經驗。
本文是「分佈式系統前沿技術」專題文章,目前該專題在持續更新中,歡迎你們保持關注👇