MOSN 多协议扩展开发实践
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 的協議擴展在生產環境落地可用。
總結
以上是生活随笔為你收集整理的MOSN 多协议扩展开发实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 参加了 Go 贡献者大会
- 下一篇: 从 wiscKey 看 LSMtree