雲原生網路代理 MOSN 的多協議機制解析

rootsongjc發表於2020-03-27

本文根據 SOFAChannel#13 直播分享整理,主題:雲原生網路代理 MOSN 多協議機制解析,檢視視訊回顧

作者:無鉤,目前主要從事螞蟻金服網路代理相關的研發工作,也是 MOSN 的 Committer。

MOSN 開源地址:https://github.com/mosn/mosn

MOSN 官網:https://mosn.io

今天我要和大家分享的是《雲原生網路代理 MOSN 多協議機制解析》,並介紹對應的私有協議快速接入實踐案例以及對 MOSN 實現多協議低成本接入的設計進行解讀。

我們將按以下順序進行介紹:

  • 多協議機制產生的背景與實踐痛點;
  • 常見的協議擴充套件思路初探;
  • SOFABolt 協議接入實踐;(重點)
  • MOSN 多協議機制設計解讀;(重點)
  • 後續規劃及展望;

其中第三點「接入實踐」是今天分享的重點,希望能給大家就「如何在 MOSN 中快速擴充套件私有協議接入」有一個具體的感受。另外「MOSN 如何實現多協議框架」也是很多人關心和問題,我們將摘選幾個技術功能,對其背後的設計思考進行解讀。

MOSN 簡介

MOSN 簡介

雲原生網路代理 MOSN 定位是一個全棧的網路代理,支援包括網路接入層 (Ingress)、API Gateway、Service Mesh 等場景,目前在螞蟻金服內部的核心業務叢集已經實現全面落地,並經受了 2019 年雙十一大促的考驗。今天要向大家介紹的是雲原生網路代理 MOSN 核心特性之一的多協議擴充套件機制,目前已經支援了包括 SOFABolt、Dubbo、TARS 等多個協議的快速接入。

MOSN:https://github.com/mosn/mosn

多協議機制產生的背景與實踐痛點

首先介紹一下多協議機制產生的背景。

多協議機制

前面提到,螞蟻金服 2019 年雙十一核心鏈路百分之百 Mesh 化,是業界當時已知的最大規模的 Service Mesh 落地,為什麼我們敢這麼做?因為我們具備能夠讓架構平滑遷移的方案。” 相容性” 是任何架構演進升級都必然要面對的一個問題,這在早已實踐微服務化架構的螞蟻金服內部同樣如此。為了實現架構的平滑遷移,需要讓新老節點的外在行為儘可能的表現一致,從而讓依賴方無感知,這其中很重要的一點就是保持協議相容性。

因此,我們需要在 Service Mesh 架構下,相容現有微服務體系中的通訊協議——也就是說需要在 MOSN 內實現對目前螞蟻金服內部通訊協議的擴充套件支援。

協議擴充套件支援

基於 MOSN 本身的擴充套件機制,我們完成了最初版本的協議擴充套件接入。但是在實踐過程中,我們發現這並不是一件容易的事情:

  • 相比編解碼,協議自身的處理以及與框架整合才是其中最困難的環節,需要理解並實現包括請求生命週期、多路複用處理、連結池等等機制;
  • 社群主流的 xDS 路由配置是面向 HTTP 協議的,無法直接支援私有協議,存在適配成本;

基於這些實踐痛點,我們設計了 MOSN 多協議框架,希望可以降低私有協議的接入成本,加快普及 ServiceMesh 架構的落地推進。

常見的協議擴充套件思路初探

前面介紹了背景,那麼具體協議擴充套件框架要怎麼設計呢?我們先來看一下業界的思路與做法。

協議擴充套件框架 - Envoy

Envoy 的協議擴充套件 注:圖片來自 Envoy 分享資料

第一個要介紹的是目前發展勢頭強勁的 Envoy。從圖上可以看出,Envoy 支援四層的讀寫過濾器擴充套件、基於 HTTP 的七層讀寫過濾器擴充套件以及對應的 Router/Upstream 實現。如果想要基於 Envoy 的擴充套件框架實現 L7 協議接入,目前的普遍做法是基於 L4 filter 封裝相應的 L7 codec,在此基礎之上再實現對應的協議路由等能力,無法複用 HTTP L7 的擴充套件框架。

協議擴充套件框架 - Nginx

Nginx 的協議擴充套件

第二個則是老牌的反向代理軟體 Nginx,其核心模組是基於 Epoll/Kqueue 等 I/O 多路複用技術之上的離散事件框架,基於事件框架之上構建了 Mail、Http 等協議模組。與 Envoy 類似,如果要基於 Nginx 擴充套件私有協議,那麼也需要自行對接事件框架,並完整實現包括編解碼、協議處理等能力。

協議擴充套件框架 - MOSN

MOSN 的協議擴充套件框架

最後回過頭來,我們看一下 MOSN 是怎麼做的。實際上,MOSN 的底層機制與 Envoy、Nginx 並沒有核心差異,同樣支援基於 I/O 多路複用的 L4 讀寫過濾器擴充套件,並在此基礎之上再封裝 L7 的處理。但是與前兩者不同的是,MOSN 針對典型的微服務通訊場景,抽象出了一套適用於基於多路複用 RPC 協議的擴充套件框架,遮蔽了 MOSN 內部複雜的協議處理及框架流程,開發者只需要關注協議本身,並實現對應的框架介面能力即可實現快速接入擴充套件。

三種框架成本對比

三種框架成本對比

最後對比一下,典型微服務通訊框架協議接入的成本,由於 MOSN 針對此類場景進行了框架層面的封裝支援,因此可以節省開發者大量的研發成本。

SOFABolt 協議接入實踐

初步瞭解多協議框架的設計思路之後,讓我們以 SOFABolt 協議為例來實際體驗一下協議接入的過程。

SOFABolt 簡介

SOFABolt 簡介

這裡先對 SOFABolt 進行一個簡單介紹,SOFABolt 是一個開源的輕量、易用、高效能、易擴充套件的 RPC 通訊框架,廣泛應用於螞蟻金服內部。

SOFABolt:https://github.com/sofastack/sofa-bolt

基於 MOSN 的多協議框架,實際編寫了 7 個程式碼檔案,一共 925 行程式碼 (包括 liscence、comment 在內) 就完成了接入。如果對於協議本身較為熟悉,且具備一定的 MOSN/Golang 開發經驗,甚至可以在一天內就完成整個協議的擴充套件,可以說接入成本是非常之低。

image.png

Github: https://github.com/mosn/mosn/tree/master/pkg/protocol/xprotocol/bolt

下面讓我們進入正題,一步一步瞭解接入過程。

Step1:確認協議格式

第一步,需要確認要接入的協議格式。為什麼首先要做這個,因為協議格式是一個協議最基本的部分,有以下兩個層面的考慮:

  • 任何協議特性以及協議功能都能在上面得到一些體現,例如有無 requestId/streamId 就直接關聯到協議是否支援連線多路複用;
  • 協議格式與報文模型直接相關,兩者可以構成邏輯上的對映關係;而這個對映關係也就是所謂的編解碼邏輯;

確認協議格式

以 SOFABolt 為例,其第一個位元組是協議 magic,可以用於校驗當前報文是否屬於 SOFABolt 協議,並可以用於協議自動識別匹配的場景;第二個位元組是 type,用於標識當前報文的傳輸型別,可以是 Request / RequestOneway / Response 中的一種;第三個位元組則是當前報文的業務型別,可以是心跳幀,RPC 請求/響應等型別。後面的欄位就不一一介紹了,可以發現,理解了協議格式本身,其實對於協議的特性支援和模型編解碼就理解了一大半,因此第一步協議格式的確認了解是重中之重,是後續一切工作開展的前提。

Step2:確認報文模型

確認報文模型

順應第一步,第二步的主要工作是確認報文程式設計模型。一般地,在第一步完成之後,應當可以很順利的構建出相應的報文模型,SOFABolt 例子中可以看出,模型欄位設計基本與協議格式中的 header / payload 兩部分相對應。有了程式設計模型之後,就可以繼續進行下一步——基於模型實現對應的框架擴充套件了。

Step3:介面實現 - 協議

介面實現-協議

協議擴充套件,顧名思義,是指協議層面的擴充套件,描述的是協議自身的行為(區別於報文自身)。

目前多協議框架提供的介面包括以下五個:

  • Name:協議名稱,需要具備唯一性;
  • Encoder:編碼器,用於實現從報文模型到協議傳輸位元組流的對映轉換;
  • Decoder:解碼器,用於實現從協議傳輸位元組流到報文模型的對映轉換;
  • Heartbeater:心跳處理,用於實現心跳保活報文的構造,包括探測發起與回覆兩個場景;
  • Hijacker:錯誤劫持,用於在特定錯誤場景下錯誤報文的構造;

Step4:介面實現 - 報文

介面實現-報文

前面介紹了協議擴充套件,接下里則是報文擴充套件,這裡關注的是單個請求報文需要實現的行為。

目前框架抽象的介面包括以下幾個:

  • Basic:需要提供 GetStreamType、GetHeader、GetBody 幾個基礎方法,分別對應傳輸型別、頭部資訊、載荷資訊;
  • Multiplexing:多路複用能力,需要實現 GetRequestId 及 SetRequestId;
  • HeartbeatPredicate:用於判斷當前報文是否為心跳幀;
  • GoAwayPredicate:用於判斷當前報文是否為優雅退出幀;
  • ServiceAware:用於從報文中獲取 service、method 等服務資訊;

舉個例子

案例

這裡舉一個例子,來讓大家對框架如何基於介面封裝處理流程有一個體感:服務端心跳處理場景。當框架收到一個報文之後:

  • 根據報文擴充套件中的 GetStreamType 來確定當前報文是請求還是響應。如果是請求則繼續 2;
  • 根據報文擴充套件中的 HeartbeatPredicate 來判斷當前報文是否為心跳包,如果是則繼續 3;
  • 當前報文是心跳探測 (request + heartbeat),需要回復心跳響應,此時根據協議擴充套件中的 Heartbeater.Reply 方法構造對應的心跳響應報文;
  • 再根據協議擴充套件的 Encoder 實現,將心跳響應報文轉換為傳輸位元組流;
  • 最後呼叫 MOSN 網路層介面,將傳輸位元組流回復給發起心跳探測的客戶端;

當協議擴充套件與報文擴充套件都實現之後,MOSN 協議擴充套件接入也就完成了,框架可以依據協議擴充套件的實現來完成協議的處理,讓我們實際演示一下 SOFABolt 接入的 example。

Demo 地址:https://github.com/mosn/mosn/tree/master/examples/codes/sofarpc-with-xprotocol-sample

MOSN 多協議機制設計解讀

通過 SOFABolt 協議接入的實踐過程,大家對如何基於 MOSN 來做協議擴充套件應該有了一個初步的認知。那麼 MOSN 多協議機制究竟封裝了哪些邏輯,背後又是如何思考設計的?接下來將會挑選幾個典型技術案例為大家進行解讀。

協議擴充套件框架

協議擴充套件框架 - 編解碼

協議擴充套件框架-編解碼

最先介紹的是編解碼機制,這個在前面 SOFABolt 接入實踐中已經簡單介紹過,MOSN 定義了編碼器及解碼器介面來遮蔽不同協議的編解碼細節。協議接入時只需要實現編解碼介面,而不用關心相應的介面呼叫上下文。

協議擴充套件框架 - 多路複用

協議擴充套件看框架-多路複用

接下來是多路複用機制的解讀,這也是流程中相對不太好理解的一部分。首先明確一下連結多路複用的定義:允許在單條連結上,併發處理多個請求/響應。那麼支援多路複用有什麼好處呢?

以 HTTP 協議演進為例,HTTP/1 雖然可以維持長連線,但是單條連結同一時間只能處理一個請求/相應,這意味著如果同時收到了 4 個請求,那麼需要建立四條 TCP 連結,而建鏈的成本相對來說比較高昂;HTTP/2 引入了 stream/frame 的概念,支援了分幀多路複用能力,在邏輯上可以區分出成對的請求 stream 和響應 stream,從而可以在單條連結上併發處理多個請求/響應,解決了 HTTP/1 連結數與併發數成正比的問題。

類似的,典型的微服務框架通訊協議,如 Dubbo、SOFABolt 等一般也都實現了連結多路複用能力,因此 MOSN 封裝了相應的多路複用處理流程,來簡化協議接入的成本。讓我們跟隨一個請求代理的過程,來進一步瞭解。

上下游關聯對映

  1. MOSN 從 downstream(conn=2) 接收了一個請求 request,依據報文擴充套件多路複用介面 GetRequestId 獲取到請求在這條連線上的身份標識 (requestId=1),並記錄到關聯對映中待用;
  2. 請求經過 MOSN 的路由、負載均衡處理,選擇了一個 upstream(conn=5),同時在這條連結上新建了一個請求流 (requestId=30),並呼叫文擴充套件多路複用介面 SetRequestId 封裝新的身份標識,並記錄到關聯對映中與 downstream 資訊組合;
  3. MOSN 從 upstream(conn=5) 接收了一個響應 response,依據報文擴充套件多路複用介面 GetRequestId 獲取到請求在這條連線上的身份標識 (requestId=30)。此時可以從上下游關聯對映表中,根據 upstream 資訊 (connId=5, requestId=30) 找到對應的 downstream 資訊 (connId=2, requestId=1);
  4. 依據 downstream request 的資訊,呼叫文擴充套件多路複用介面 SetRequestId 設定響應的 requestId,並回復給 downstream;

在整個過程中,框架流程依賴的報文擴充套件 Multiplexing 介面提供的能力,實現了上下游請求的多路複用關聯處理,除此之外,框架還封裝了很多細節的處理,例如上下游複用記憶體塊合併處理等等,此處限於篇幅不再展開,有興趣的同學可以參考原始碼進行閱讀。

統一路由框架

統一路由框架

接下來要分析的是「統一路由框架」的設計,此方案主要解決的是非 HTTP 協議的路由適配問題。我們選取了以下三點進行具體分析:

  • 通過基於屬性匹配 (attribute-based) 的模式,與具體協議欄位解耦;
  • 引入層級路由的概念,解決屬性扁平化後帶來的線性匹配效能問題;
  • 通過變數機制懶載入的特定,按需實現深/淺解包;

統一路由框架 – 基於屬性匹配

統一路由框架-基於屬性匹配

首先來看一下典型的 RDS 配置,可以看到其中的 domains、path 等欄位,對應的是 HTTP 協議裡的域名、路徑概念,這就意味著其匹配條件只有 HTTP 協議才有欄位能夠滿足,配置結構設計是與 HTTP 協議強相關的。這就導致瞭如果我們新增了一個私有協議,無法複用 RDS 的配置來做路由。

那麼如何解決配置模型與協議欄位強耦合呢?簡單來說就是把匹配欄位拆分為扁平屬性的鍵值對 (key-value pair),匹配策略基於鍵值對來處理,從而解除了匹配模型與協議欄位的強耦合,例如可以配置 key: $http_host,也可以配置 key:$dubbo_service,這在配置模型層面都是合法的。

但是這並不是說匹配就有具體協議無關了,這個關聯仍然是存在的,只是從強耦合轉換為了隱式關聯,例如配置 key: $http_host,從結構來說其與 HTTP 協議並無耦合,但是值變數仍然會通過 HTTP 協議欄位來進行求值。

統一路由框架 - 層級路由

統一路由框架 -  層級路由

在引入「基於屬性的匹配」之後,我們發現了一個問題,那就是由於屬性本身的扁平化,其內在並不包含層級關係。如果沒有層級關係,會導致匹配時需要遍歷所有可能的情況組合,大量條件的場景下匹配效能近似於線性的 O(n),這顯然是無法接受的。

舉例來說,對於 HTTP 協議,我們總是習慣與以下的匹配步驟:

  • 匹配 Host(:authority) ;
  • 匹配 Path ;
  • 匹配 headers/args/cookies ;

這其實構成了一個層級關係,每一層就像是一個索引,通過層級的索引關係,在大量匹配條件的情況下仍然可以獲得一個可接受的耗時成本。但是對於屬性 (attribute),多個屬性之間並沒有天然的層級關係 (相比於 host、path 這種欄位),這依賴於屬性背後所隱式關聯的欄位,例如對於 Dubbo 協議,我們希望的順序可能是:

  • 匹配 $dubbo_service
  • 匹配 $dubbo_group
  • 匹配 $dubbo_version
  • 匹配 $dubbo_attachments_xx

因此在配置模型上,我們引入了對應的索引層級概念,用於適配不同協議的結構化層級路由,解決扁平屬性的線性匹配效能問題。

統一路由框架 - 淺解包優化

統一路由框架 - 淺解包優化

最後,介紹一下淺解包優化的機制。利用 MOSN 變數懶載入的特性,我們可以在報文解析時,先不去解析成本較高的部分,例如 dubbo 協議的 attachments。那麼在代理請求的實際過程中,需要使用到 attachments 裡的資訊時,就會通過變數的 getter 求值邏輯來進行真正的解包操作。依靠此特性,可以大幅優化在不需要深解包的場景下 dubbo 協議代理轉發的效能表現,實現按需解包。

解讀總結

最後,對設計部分的幾個技術案例簡單總結一下,整體的思路仍然是對處理流程進行抽象封裝,並剝離可擴充套件點,從而降低使用者的接入成本。

在協議擴充套件支援方面:

  • 封裝編解碼流程,抽象編解碼能力介面作為協議擴充套件點
  • 封裝協議處理流程,抽象多路複用、心跳保活、優雅退出等能力介面作為協議擴充套件點

在路由框架方面:

  • 通過改為基於屬性匹配的機制,與具體協議欄位解耦,支援多協議適配;
  • 引入層級路由機制,解決屬性扁平化的匹配效能問題;
  • 利用變數機制懶載入特性,按需實現深/淺解包;

後續規劃及展望

更多流模式支援、更多協議接入

更多流模式支援、更多協議接入

當前 MOSN 多協議機制,已經可以比較好的支援像 Dubbo、SOFABolt 這樣基於多路複用流模型的微服務協議,後續會繼續擴充套件支援的型別及協議,例如經典的 PING-PONG 協議、Streaming 流式協議,也歡迎大家一起參與社群建設,貢獻你的 PR。

社群標準方案推進

社群標準方案推進

與此同時,我們注意到 Istio 社群其實也有類似的需求,希望設計一套協議無關的路由機制——” Istio Meta Routing API”。其核心思路與 MOSN 的多協議路由框架基本一致,即通過基於屬性的路由來替代基於協議欄位的路由。目前該草案還處於一個比較初級的階段,對於匹配效能、欄位擴充套件方面還沒有比較完善的設計說明,後續 MOSN 團隊會積極參與社群方案的討論,進一步推動社群標準方案的落地。

以上就是本期分享的全部內容,如果大家對 MOSN 有問題以及建議,歡迎加入 MOSN 社群與我們交流。

更多原創文章乾貨分享,請關注公眾號
  • 雲原生網路代理 MOSN 的多協議機制解析
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章