1. 程式人生 > >SOFAMesh中的多協議通用解決方案x-protocol介紹系列(2):快速解碼轉發

SOFAMesh中的多協議通用解決方案x-protocol介紹系列(2):快速解碼轉發

2018年上半年,螞蟻金服決定基於 Istio 訂製自己的 ServiceMesh 解決方案,並在6月底正式對外公佈了 SOFAMesh,詳情可直接點選之前的文章檢視:大規模微服務架構下的Service Mesh探索之路 。 在 SOFAMesh 的開發過程中,針對遇到的實際問題,我們給出了一套名為 x-protocol 的解決方案,本文將會對這個解決方案進行詳細的講解,後面會有更多內容,歡迎持續關注本系列文章。 上一篇:SOFAMesh中的多協議通用解決方案x-protocol介紹系列(1) : DNS通用定址方案 1545375431822-9139c720-5386-4f38-926b-99 前言 在 Istio 和 Envoy 中,對通訊協議的支援,主要體現在 HTTP/1.1和 HTTP/2上,而我們 SOFAMesh,則需要支援以下幾個 RPC 協議: SOFARPC:這是螞蟻金服大量使用的RPC協議(已開源) HSF RPC:這是阿里集團內部大量使用的RPC協議(未開源) Dubbo RPC: 這是社群廣泛使用的RPC協議(已開源) 更適合的平衡點:效能和功能 對於服務間通訊解決方案,效能永遠是一個值得關注的點。而 SOFAMesh 在專案啟動時就明確要求在效能上要有更高的追求,為此,我們不得不在 Istio 標準實現之外尋求可以獲取更高效能的方式,比如支援各種 RPC 協議。 期間有兩個發現: 1.Istio 在處理所有的請求轉發如 REST/gRPC 時,會解碼整個請求的 header 資訊,拿到各種資料,提取為 Attribute,然後以此為基礎,提供各種豐富的功能,典型如 Content Based Routing。 2.而在測試中,我們發現:解碼請求協議的 header 部分,對 CPU 消耗較大,直接影響效能。 因此,我們有了一個很簡單的想法:是不是可以在轉發時,不開啟部分功能,以此換取轉發過程中的更少更快的解碼消耗?畢竟,不是每個服務都需要用到 Content Based Routing 這樣的高階特性,大部分服務只使用 Version Based Routing,尤其是使用 RPC 通訊協議的服務,沒有HTTP那麼表現力豐富的 header,對 Content Based Routing 的需求要低很多。 此外,對於部分對效能有極高追求的服務,不開啟高階特性而換取更高的效能,也是一種滿足效能要求的折中方案。考慮到系統中總存在個別服務對效能非常敏感,我們覺得 Service Mesh 提供一種效能可以接近直連的方案會是一個有益的補充。為了滿足這些特例而不至於因此整體否決 Service Mesh 方案,我們需要在 Service Mesh 的大框架下提供一個折中方案。
請求轉發
在我們進一步深入前,我們先來探討一下實現請求轉發的技術細節。 有一個關鍵問題:當 Envoy/SOFA MOSN 這樣的代理程式,接收到來自客戶端的TCP 請求時,需要獲得哪些資訊,才可以正確的轉發請求到上游的伺服器端? 最關鍵的資訊:destination 首先,毫無疑問的,必須拿到 destination 目的地,也就是客戶端請求必須通過某種方式明確的告之代理該請求的 destination,這樣代理程式才能根據這個 destionation去找到正確的目標伺服器,然後才有後續的連線目標伺服器和轉發請求等操作。 1545375451226-81d2d5da-cef7-4b3c-9e54-0c
Destination 資訊的表述形式可能有: 1.IP地址 可能是伺服器端例項實際工作的 IP 地址和埠,也可能是某種轉發機制,如Nginx/HAProxy 等反向代理的地址或者 Kubernetes 中的 ClusterIP。
舉例:“192.168.1.1:8080”是實際IP地址和埠,“10.2.0.100:80”是 ngxin 反向代理地址,“172.168.1.105:80”是Kubernetes的ClusterIP。 2.目標服務的識別符號
可用於名字查詢,如服務名,可能帶有各種字首字尾。然後通過名字查詢/服務發現等方式,得到地址列表(通常是IP地址+埠形式)。 舉例:“userservice”是標準服務名, “com.alipay/userservice”是加了域名字首的服務名, “service.default.svc.cluster.local”是k8s下完整的全限定名。 Destination資訊在請求報文中的攜帶方式有: 1.通過通訊協議傳遞 這是最常見的形式,標準做法是通過header頭,典型如HTTP/1.1下一般使用 host header,舉例如“Host: userservice”。HTTP/2下,類似的使用“:authority” header。 對於非HTTP協議,通常也會有類似的設計,通過協議中某些欄位來承載目標地址資訊,只是不同協議中這個欄位的名字各有不同。如SOFARPC,HSF等。 有些通訊協議,可能會將這個資訊存放在payload中,比如後面我們會介紹到的dubbo協議,導致需要反序列化payload之後才能拿到這個重要資訊。
2.通過TCP協議傳遞 這是一種非常特殊的方式,通過在TCP option傳遞,上一節中我們介紹Istio DNS定址時已經詳細介紹過了。 TCP拆包 如何從請求的通訊協議中獲取destination?這涉及到具體通訊協議的解碼,其中第一個要解決的問題就是如何在連續的TCP報文中將每個請求內容拆分開,這裡就涉及到經典的TCP沾包、拆包問題。 1545375470404-88ca40e3-d10d-4c1b-88de-6d 轉發請求時,由於涉及到負載均衡,我們需要將請求傳送給多個伺服器端例項。因此,有一個非常明確的要求:就是必須以單個請求為單位進行轉發。即單個請求必須完整的轉發給某臺伺服器端例項,負載均衡需要以請求為單位,不能將一個請求的多個報文包分別轉發到不同的伺服器端例項。所以,拆包是請求轉發的必備基礎。 由於篇幅和主題限制,我們不在這裡展開TCP沾包、拆包的原理。後面針對每個具體的通訊協議進行分析時再具體看各個協議的解決方案。 多路複用的關鍵引數:RequestId RequestId用來關聯request和對應的response,請求報文中攜帶一個唯一的id值,應答報文中原值返回,以便在處理response時可以找到對應的request。當然在不同協議中,這個引數的名字可能不同(如streamid等)。 嚴格說,RequestId對於請求轉發是可選的,也有很多通訊協議不提供支援,比如經典的HTTP1.1就沒有支援。但是如果有這個引數,則可以實現多路複用,從而可以大幅度提高TCP連線的使用效率,避免出現大量連線。稍微新一點的通訊協議,基本都會原生支援這個特性,比如SOFARPC、Dubbo、HSF,還有HTTP/2就直接內建了多路複用的支援。 HTTP/1.1不支援多路複用(http1.1有提過支援冪等方法的pipeline機制但是未能普及),用的是經典的ping-pong模式:在請求傳送之後,必須獨佔當前連線,等待伺服器端給出這個請求的應答,然後才能釋放連線。因此HTTP/1.1下,併發多個請求就必須採用多連線,為了提升效能通常會使用長連線+連線池的設計。而如果有了requestid和多路複用的支援,客戶端和Mesh之間理論上就可以只用一條連線(實踐中可能會選擇建立多條)來支援併發請求: 1545375606200-e66f5e46-c444-4aff-b367-70 而Mesh與伺服器(也可能是對端的Mesh)之間,也同樣可以受益於多路複用技術,來自不同客戶端而去往同一個目的地的請求可以混雜在同一條連線上傳送。通過RequestId的關聯,Mesh可以正確將reponse傳送到請求來自的客戶端。 1545375619011-a75c8045-d8b2-42c4-b036-90 由於篇幅和主題限制,我們不在這裡展開多路複用的原理。後面針對每個具體的通訊協議進行分析時再具體看各個協議的支援情況。 請求轉發引數總結 上面的分析中,我們可以總結到,對於Sidecar,要正確轉發請求: 必須獲取到destination資訊,得到轉發的目的地,才能進行服務發現類的定址 必須要能夠正確的拆包,然後以請求為單位進行轉發,這是負載均衡的基礎 可選的RequestId,這是開啟多路複用的基礎 因此,這裡我們的第一個優化思路就出來了:儘量只解碼獲取這三個資訊,滿足轉發的基本要求。其他資訊如果有效能開銷則跳過解碼,所謂“快速解碼轉發”。基本原理就是犧牲資訊完整性追求效能最大化。 而結合上一節中我們引入的DNS通用定址方案,我們是可以從請求的TCP options中得到ClusterIP,從而實現定址。這個方式可以實現不解碼請求報文,尤其是header部分解碼destination資訊開銷大時。這是我們的第二個優化思路:跳過解碼destination資訊,直接通過ClusterIP進行定址。 具體的實現則需要結合特定通訊協議的實際情況進行。 主流通訊協議 現在我們開始,以Proxy、Sidecar、Service Mesh的角度來看看目前主流的通訊協議和我們前面列舉的需要在SOFAMesh中支援的幾個協議。 SOFARPC/bolt協議 SOFARPC 是一款基於 Java 實現的 RPC 服務框架,詳細資料可以查閱 官方文件。SOFARPC 支援 bolt,rest,dubbo 協議進行通訊。REST、dubbo後面單獨展開,這裡我們關注bolt協議。 bolt 是螞蟻金服集團開放的基於 Netty 開發的網路通訊框架,其協議格式是變長,即協議頭+payload。具體格式定義如下,以request為例(response類似): 1545375674030-b59d7b5a-c7aa-472e-83a3-55 我們只關注和請求轉發直接相關的欄位: TCP拆包 bolt協議是定長+變長的複合結構,前面22個位元組長度固定,每個位元組和協議欄位的對應如圖所示。其中classLen、headerLen和contentLen三個欄位指出後面三個變長欄位className、header、content的實際長度。和通常的變長方案相比只是變長欄位有三個。拆包時思路簡單明瞭: 先讀取前22個位元組,解出各個協議欄位的實際值,包括classLen,headerLen和contentLen 按照classLen、headerLen和contentLen的大小,繼續讀取className、header、content Destination Bolt協議中的header欄位是一個map,其中有一個key為“service”的欄位,傳遞的是介面名/服務名。讀取稍微麻煩一點點,需要先解碼整個header欄位,這裡對效能有影響。 RequestId Blot協議固定欄位中的requestID欄位,可以直接讀取。 SOFARPC中的bolt協議,設計的比較符合請求轉發的需要,TCP拆包,讀取RequestID,都沒有效能問題。只是Destination的獲取需要解碼整個header,效能開銷稍大。 總結:適合配合DNS通用解碼方案,跳過對整個header部分的解碼,從而提升效能。當然由於這個header本身也不算大,優化的空間有限,具體提升需要等對比測試的結果出來。 HSF協議 HSF協議是經過精心設計工作在4層的私有協議,由於該協議沒有開源,因此不便直接暴露具體格式和欄位詳細定義。 不過基本的設計和bolt非常類似:
  • 採用變長格式,即協議頭+payload
  • 在協議頭中可以直接拿到服務介面名和服務方法名作為Destination
  • 有RequestID欄位
基本和bolt一致,考慮到Destination可以直接讀取,比bolt還要方便一些,HSF協議可以說是對請求轉發最完美的協議。 總結:目前的實現方案也只解碼了這三個關鍵欄位,速度足夠快,不需要繼續優化。 Dubbo協議 Dubbo協議也是類似的協議頭+payload的變長結構,其協議格式如下: 1545375689105-79e89667-f14b-46be-be58-3f 其中long型別的id欄位用來把請求request和返回的response對應上,即我們所說的RequestId。

相關推薦

SOFAMesh協議通用解決方案x-protocol介紹系列2快速解碼轉發

2018年上半年,螞蟻金服決定基於 Istio 訂製自己的 ServiceMesh 解決方案,並在6月底正式對外公佈了 SOFAMesh,詳情可直接點選之前的文章檢視:大規模微服務架構下的Service Mesh探索之路 。 在 SOFAM

SOFAMesh協議通用解決方案x-protocol介紹系列(1) DNS通用定址方案

小螞蟻說: 2018年上半年,螞蟻金服決定基於 Istio 訂製自己的 ServiceMesh 解決方案,並在6月底正式對外公佈了 SOFAMesh 。 在 SOFAMesh 的開發過程中,針對遇到的實際問題,我們給出了一套名為 x-protocol 的解決方案,本文將會對這個解決方案進

研究些架構,少談些框架 2 微服務和充血模型

方法 平時 是把 小系統 生涯 過程 語句 小結 大量 上篇我們聊了微服務的DDD之間的關系,很多人還是覺得很虛幻,DDD那麽復雜的理論,聚合根、值對象、事件溯源,到底我們該怎麽入手呢? 實際上DDD和面向對象設計、設計模式等等理論有千絲萬縷的聯系,如果不熟悉OOA、OOD

爬蟲入門系列快速理解HTTP協議

爬蟲入門系列目錄: 4月份給自己挖一個爬蟲系列的坑,主要涉及HTTP 協議、正則表示式、爬蟲框架 Scrapy、訊息佇列、資料庫等內容。 爬蟲的基本原理是模擬瀏覽器進行 HTTP 請求,理解 HTTP 協議是寫爬蟲的必備基礎,招聘網站的爬蟲崗位也赫然寫著熟練掌握HTTP協議規範,寫

Netty4.x 原始碼實戰系列深入淺出學Pipeline

在netty中,每一個channel都有一個pipeline物件,並且其內部本質上就是一個雙向連結串列 本篇我們將深入Pipeline原始碼內部,對其一探究竟,給大家一個全方位解析。 Pipeline初始化過程分析 在上一篇中,我們得知ch

Netty4.x 原始碼實戰系列NioServerSocketChannel全剖析

根據上一篇《Netty4.x 原始碼實戰系列(二):服務端bind流程詳解》所述,在進行服務端開發時,必須通過ServerBootstrap引導類的channel方法來指定channel型別, channel方法的呼叫其實就是例項化了一個用於生成此channel

Netty4.x 原始碼實戰系列服務端bind流程詳解

在上一篇《ServerBootstrap 與 Bootstrap 初探》中,我們已經初步的瞭解了ServerBootstrap是netty進行服務端開發的引導類。 且在上一篇的服務端示例中,我們也看到了,在使用netty進行網路程式設計時,我們是通過bind方法

物聯網平臺構架系列 Amazon, Microsoft, IBM IoT 解決方案導論 之 結語

物聯網; iot; aws; 亞馬遜; greengrass;microsoft; azure;ibm; watson; bluemix最近研究了一些物聯網平臺技術資料,以做選型參考。腦子裏積累大量信息,便想寫出來做一些普及。作為科普文章,力爭通俗易懂,不確保概念嚴謹性。我會給考據癖者提供相關英文鏈接,以便深

02-linux-arm板上opencv移植--終極解決方案之buildroot基礎配置原創

接前一篇《迅為4412-linux-arm板上opencv移植–終極解決方案(原創)》。 平臺:Exynos4412。 實驗平臺:iTOP-4412-精英版。 編譯平臺:Ubuntu12.04。 編譯器版本:arm-4.4.1,懶人直接用的開發板自帶的。 buildroot版本:直接

ABP從入門到精通6快速重新命名解決方案

SolutionRenamer SolutionRenamer 是一個解決方案快速重新命名工具。經測試重新命名一個全新asp.net zero core專案(ABP asp.net zero,.net core版本,版本號4.4.0),耗時大約在3s左右。 使用 這個zip

ABP框架一對關係的處理以及功能介面的處理2

在我們開發業務的時候,一般資料庫表都有相關的關係,除了單獨表外,一般還包括一對多、多對多等常見的關係,在實際開發過程中,需要結合系統框架做對應的處理,本篇隨筆介紹基於ABP框架對EF實體、DTO關係的處理,以及提供對應的介面進行相關的資料儲存更新操作,這篇介紹多對多關係下的ABP框架的處理。 上篇隨筆《A

TCP/IP協議2網絡設備

數據包 服務器 網絡設備 風暴 二層交換機 不同的 中繼器 tcp/ip 解決 1、中繼器(Repeater) 中繼器工作在OSI的一層,我們知道,超5類線的傳輸距離最大為100米,超過這個距離信號就會衰減,中繼器就是為了防止信號變差,將網絡信號進行再生和重定時。 2、

5.27cocos2d-x初探學習筆記2--重要概念及Test樣例結構(轉)

這樣的 發生 菜單 add css 基礎 dsm 人的 添加 1.幾個重要概念 在cocos2d引擎中,有幾個概念,各自是導演。場景,布景和人物角色。 導演(CCDirector):在cocos2d-x引擎中,導演類是遊戲的組織者和領導者。導演制定規則讓遊戲內的場

Java學習2將鍵盤錄入的內容保存到指定文件

stream exce 創建 txt 關閉 如果 下午 line 再次 要求:保存鍵盤錄入的內容,當鍵盤輸入end時,錄入結束。 1 /** 2 * 保存鍵盤輸入,並以end結束 3 * 4 * @author xcx 5 * @time 2017年6

挖一挖C#那些我們不經常使用的東西之系列4——GetHashCode,ExpandoObject

add 工具 通過 border 後期綁定 main image 代碼 不同 一:GetHashCode   從MSDN上能夠看到的解釋是:用作特定類型的哈希函數,也就是說不論什麽對象的實例都會有一個int32類型的HashCode。而且存放在FCL中的

線程面試題系列5經典線程同步 關鍵段CS

得到 bug oar -- 多線程同步 實現 unsigned 初始化 alt 上一篇提出了一個經典的多線程同步互斥問題,本篇將用關鍵段CRITICAL_SECTION來嘗試解決這個問題。本文首先介紹下如何使用關鍵段,然後再深層次的分析下關鍵段的實現機制與原理。關鍵段CRI

線程面試題系列15關鍵段,事件,互斥量,信號量的“遺棄”問題

creating 不為 char toc 效果 創建 cti 不能 false 一.什麽是“遺棄”問題 在第七篇講到了互斥量能處理“遺棄”問題,下面引用原文: 互斥量常用於多進程之間的線程互斥,所以它比關鍵段還多一個很有用的特性——“遺棄”情況的處理。比如有一個占用互斥量的

線程面試題系列14讀者寫者問題繼 讀寫鎖SRWLock

線程面試題 oid out 讀者寫者問題 五個 lock val ref win7 在第十一篇文章中我們使用事件和一個記錄讀者個數的變量來解決讀者寫者問題。問題雖然得到了解決,但代碼有點復雜。本篇將介紹一種新方法——讀寫鎖SRWLock來解決這一問題。讀寫鎖在對資源進行保

線程面試題系列16線程十大經典案例之一 雙線程讀寫隊列數據

als single 間隔 eas 講解 art ces 依賴 ini 前十五篇中介紹多線程的相關概念,多線程同步互斥問題(第四篇)及解決多線程同步互斥的常用方法——關鍵段、事件、互斥量、信號量、讀寫鎖。為了讓大家更加熟練運用多線程,將會有十篇文章來講解十個多線程使用案例,

[轉]微信小程序之加載更分頁加載實例 —— 微信小程序實戰系列2

是否 底部 watermark water ongl 小程序教程 所有 空數組 osi 本文轉自;http://blog.csdn.net/michael_ouyang/article/details/56846185 loadmore 加載更多(分頁加載) 當