Service Mesh 是當今雲原生的關鍵部分,螞蟻已經在生產環境完成了大規模的落地,但是業界整體 Service Mesh 改造程度還不高。其中平穩的進行 Mesh 化改造是可以對已上線的業務進行 Mesh 化改造的前提,在平穩改造過程中,協議的支援又是最基礎的部分。MOSN 提供的多協議擴充套件開發框架旨在降低使用私有協議的場景進行 Mesh 化改造的成本,幫助業務快速落地。
MOSN 是螞蟻自研的一款高效能網路代理,主要用於 Service Mesh 的資料面 Sidecar。Service Mesh,是近幾年來雲原生方向比較熱門的話題,其主旨就是構建一個基礎設施層,用來負責服務之間的通訊。主要就是有一個和服務應用共同部署的 Sidecar 來實現各種中介軟體的基礎能力,實現基礎設施的標準化、和業務邏輯解耦,做到業務無感知的基礎能力快速演進。目前國內很多公司都開始擁抱 Service Mesh,和螞蟻合作的一些企業,如中信銀行、江西農信等也基於 MOSN 完成了 Mesh 化的改造。
Service Mesh 架構的目的就是為了降低基礎設施改造升級對業務造成的影響,但是如何平滑的從傳統微服務架構轉向 Service Mesh 架構也是一個非常有挑戰的工作,這裡涉及的細節很多,但是無論如何有一個最基礎的問題就是我們在進行灰度 Mesh 化改造的時候,已經 Mesh 化的節點需要能和沒有 Mesh 化的節點維持正常通訊。而不同的公司選擇的通訊協議都有所不同,這就直接導致在技術選型的時候,選擇的 Sidecar 需要能夠支援所使用的協議。一些受到廣泛應用的協議可能還會被陸續的支援,而有的協議可能還是公司自己定製的,因此不可避免的是需要基於 Sidecar 的擴充套件能力,進行二次開發以支援私有的協議。
多協議擴充套件框架
談到 Service Mesh 的 Sidecar,就不得不提到 Envoy,這是一款被廣泛應用的 Service Mesh Sidecar 代理。Envoy 的擴充套件框架支援開發者進行二次開發擴充套件,其中 Envoy 目前支援的不少協議就是基於其擴充套件框架開發實現的。在 Envoy 的擴充套件框架下,要擴充套件一個協議可以參考 Envoy 中 HTTP 協議處理的流程,包括 4 層 Filter 實現編解碼部分與 Connection Manager 部分,在 Connection Manager 的基礎上再實現 7 層的 Filter 用於支援額外的業務擴充套件、路由的能力、和 Upstream 的連線池能力。可以看到一個協議處理的流程幾乎是貫穿了各種模組,實現一個協議擴充套件成本還是比較高的。
再來看一下 MOSN 的框架。MOSN 在一次協議處理上可以劃分為四個層次,除開基本的從網路 IO 中獲取資料的網路層以外,還可以劃分為 protocol 層、stream 層與 proxy 層。其中 protocol 層負責協議解析相關編解碼的工作,負責將資料流解析成 MOSN 可以理解的協議幀,或者將協議幀編碼成二進位制流;stream 層負責的內容就比較多了,包括處理不同的請求型別,初始化請求的上下文,關聯事件,響應與請求之間的關聯,還有 upstream 連線池相關的處理等,不同的協議處理的細節也會有所不同;proxy 層是一個協議無關的代理轉發層,負責請求的路由與負責均衡等能力,同時也具備七層的擴充套件能力用於不同業務實現的擴充套件。根據這個架構,可以看到協議處理的核心就在於 protocol 層和 stream 層,相比於 Envoy 的設計來說,路由、七層擴充套件等部分是具備多協議複用的能力的。但是同時也可以看到 stream 層涉及的細節比較多,實現起來難度也是比較大的,為此 MOSN 在此基礎上又提出了一個多協議擴充套件的框架,用於簡化協議的實現。
MOSN 的多協議框架主要就是針對 stream 層的複用擴充套件能力,在 MOSN 的協議處理分層設計中, network 層和 proxy 層在設計上就是協議無關可複用的,如果能做到 stream 層也進行復用,那麼協議實現就只需要關注 protocol 層的編解碼本身,實現難度就會大大降低了。那麼 stream 層是不是具備可複用的能力的呢,我們認為對於大部分協議,尤其是 RPC 協議來說是可以的。首先我們對協議進行一個抽象,定義成 XProtocol 介面,表示任意的協議。每個協議實現都是實現一個 XProtocol 介面,它包括基礎的編解碼介面、一些特殊請求響應的構造介面(如心跳、異常)、還有協議的模型(如類似 HTTP 的 pingpong 模型,常見的 RPC 多路複用模型等),以及協議匹配的介面。所有的 XProtocol 協議實現通過 XProtocol Engine 關聯起來,除了通過配置指定使用哪種協議進行處理以外,對於實現了協議匹配介面的協議來說,可以基於請求特徵進行自動識別。然後我們對於 XProtocol 解析出的協議幀也進行統一的抽象,包括多路複用相關的介面、協議型別的判斷(是請求,還是響應,或者是類似 Goaway 一類的控制幀,請求又可以細分為心跳請求、無響應的 oneway 請求等)、支援對協議幀的資料進行修改(Header/Body 的修改)、還有統一的狀態碼管理對映等。
在 MOSN 的協議處理分層機制下,以及有了以 XProtocol 和 XFrame 的抽象定義為核心的多協議擴充套件框架以後,我們在 stream 層就可以完全基於介面進行協議的處理,而不同的協議擴充套件實現者只需要專注於協議編解碼本身,以及對編解碼後的結果進行簡單的介面適配,就可以完成在 MOSN 中的接入,由此獲得 MOSN 中各種通用能力的支援,如限流擴充套件、路由引流等。對比 Envoy 中擴充套件協議實現部分可以看到是簡化了不少的。當然 MOSN 這個多協議框架不能滿足所有的協議情況,但是對於目前我們看到的大部分 RPC 協議,在配合上 proxy 層中七層 stream filter 擴充套件的基礎上,都是可以很好的滿足的。
實踐案例
下面以 MOSN 在社群合作伙伴中 Dubbo 協議落地的案例來詳細的瞭解 MOSN 的多協議擴充套件。這裡很多程式碼也是 MOSN 社群的同學貢獻的。 在這個案例中,除了要求協議需要支援 Dubbo 以外,還希望使用像限流等這些基礎的擴充套件能力,同時需要藍綠分組等路由的能力,選擇的控制面是 Istio,用於動態配置的下發。那這些需求在 MOSN 中是如何實現的呢?
首先是協議解析部分,這裡採用了基於開源的 dubbo-go 框架做協議實現,基於 dubbo-go 封裝出了 MOSN 的 XProtocol 和 XFrame 模型;限流、xDS 等能力直接複用 MOSN 已有的實現,無需額外實現。但是這裡有一個問題點就是 Istio 動態下發的路由配置是 HTTP 相關的,HTTP 的路由配置模型與 Dubbo 還是存在一定差異的,而修改 Istio 的成本會比較高,在這裡就做了另外一層擴充套件。基於 MOSN 七層的 StreamFilters,在進行路由匹配之前對 Dubbo 協議進行定製化的處理,用來滿足 HTTP 的路由格式。主要有兩個點,一個是 HTTP 的 Host,使用 Dubbo 的 Service 對應到 Host,另外一個是 HTTP 的 Path,這部分就直接新增一個預設的 Path 進行通配;同時在這個擴充套件 Filter 中,還會獲取機器的 Labels 新增到 Header 中,用於匹配路由的藍綠分組功能。通過 MOSN Filter 擴充套件的配合,我們在實現了標準的 Dubbo 協議支援的基礎上,滿足了使用 HTTP 路由配置方式滿足 Dubbo 路由的功能。
外掛化擴充套件
通過 MOSN 多協議框架的介紹,可以瞭解到當一個場景需要接入 MOSN 還不支援的自定義協議的時候,就需要進行擴充套件實現,然後和 MOSN 的框架程式碼一起進行編譯,獲得一個支援自定義協議的 MOSN。雖然已經儘量簡化了協議擴充套件實現的複雜度,但是依然會存在一些問題,比如我們商業版的程式碼中,不同的合作伙伴,對應不同的場景,使用的都是不同的協議,那麼隨著業務的發展,協議這部分相關的程式碼就會越來越多,對應的開發維護代價也會變大。這都還好說,還有一些場景,客戶協議一些細節出於某些需求可能並不想把相關的實現程式碼提交到 MOSN 的倉庫中,而商業版程式碼不同於開源的,可能也不能直接將原始碼交給客戶,那這裡編譯就會遇到問題。為了解決這種矛盾,也為了更進一步讓協議擴充套件變得簡單,MOSN 還做了基於外掛模式擴充套件協議的能力。
MOSN 外掛擴充套件模式架構如圖,MOSN 提供統一的外掛擴充套件框架能力,MOSN 獨立編譯成二進位制以後,利用外掛機制動態載入不同的協議擴充套件外掛,來獲得對應的協議支援能力。這樣協議擴充套件的實現也可以以外掛的形式獨立維護,甚至更進一步還可能支援非 Go 語言的擴充套件,比如基於 WASM 擴充套件能力對接其他語言實現的協議擴充套件。MOSN 的外掛擴充套件能力有兩種模式,一個是基於 Go Plugin 機制的擴充套件,一個是基於 WASM 的擴充套件。
首先來看一下 Go Plugin 的機制。這是 Go 語言提供的一種 SO 的載入能力,我們可以把 Go 編寫的程式碼編譯成 SO,然後可以被其他 Go 檔案載入。但是這個機制有一些比較大的侷限性,首先一點就是主程式與擴充套件外掛編譯的 Go 環境必須一致,包括 Go 的版本,GoPath 等環境變數;另外一點就是主程式與擴充套件外掛依賴的庫必須一致,這個是精確到 Hash 的,就是說不是兩個庫介面是相容就可以,而是必須一模一樣,這個限制就比較大了。因為我們預期是 MOSN 的程式碼和協議擴充套件的程式碼互相獨立維護,協議擴充套件程式碼是需要依賴 MOSN 框架的,按照 Go Plugin 的機制每次 MOSN 框架的程式碼改動都會要求外掛的程式碼也同步更新再重新編譯外掛,這個太麻煩了。
為此,我們將 MOSN 框架進行了拆解,拆分出一些相對穩定的介面和通用能力,作為 MOSN 主程式和協議擴充套件共同依賴的基礎,如 XProtocol 和 XFrame 相關的 interface 定義單獨定義到了 API 這個庫中,然後在外掛載入的時候,只需要將對應的一些介面註冊到 MOSN 框架中就可以了。由於 API 定義和工具變動相對較少,協議擴充套件外掛依賴從 MOSN 框架變成 MOSN 的 API 定義,最大程度的減少 MOSN 框架程式碼更新導致的外掛程式碼必須更新的情況。而對於編譯環境這個就好辦了,我們提供了一個統一的、用於編譯的 docker 環境,只需要讓 MOSN 和外掛都基於同樣的 docker 編譯就可以。通過 Go Plugin 實現的外掛擴充套件和直接將程式碼合併然後編譯的結果是一樣的,只是讓協議擴充套件的程式碼可以獨立進行維護。
再來看一下 WASM 的擴充套件機制。簡單介紹一下 WASM,它是一個開發、高效、安全,並且擁有社群統一標準的一種擴充套件能力,WASM 理論上是語言無關的一種能力,而且有一個被廣泛認可的網路代理場景的 ABI 規範,因為 MOSN 也支援了 WASM 的擴充套件能力,它也能用於 MOSN 的協議擴充套件。
MOSN 的 WASM 擴充套件詳細介紹可以參考【WebAssembly 在 MOSN 中的實踐 - 基礎框架篇】
基於 WASM 的協議擴充套件方式,與之前提到的協議擴充套件有所區別。WASM 外掛需要實現的是 proxy-wasm 的 ABI 標準,而不用關心 MOSN 的多協議框架,也就是說這個 WASM 外掛理論上是還可以被用於其他遵循 proxy-wasm ABI 規範的應用的。而在 MOSN 側,則是實現了一個名為 WASM Protocol 的膠水層,這個膠水層實現了 MOSN 多協議框架的封裝,然後通過 WASM ABI 與 WASM 外掛進行互動。在 MOSN 多協議框架的視角下,看到的是一個由 WASM Protocol 封裝的 XProtocol 實現,多協議框架也不理解 WASM ABI 互動的部分。和 GoPlugin 擴充套件不同,目前 MOSN 的 WASM 框架也還處於初級階段,基於 WASM 的協議擴充套件也還只有 POC,而出於穩定性、效能等多方面因素的考慮,還沒有正式應用在生產環境中,還需要更多的優化和測試。
展望
最後談一下 MOSN 在協議支援上後續的一些展望和計劃,主要就是包括更多型別的協議支援,如存在流式、雙工形式的 gRPC 協議、訊息型別的協議如卡夫卡、MQ 等,還有就是將 WASM 的協議擴充套件在生產環境落地可用。
本週推薦閱讀
更多文章請掃碼關注“金融級分散式架構”公眾號