Dubbo Mesh 在閒魚生產環境中的落地實踐

芊寶寶發表於2019-03-14
Dubbo Mesh 在閒魚生產環境中的落地實踐


本文作者至簡曾在 2018 QCon 上海站以《Service Mesh 的本質、價值和應用探索》為題做了一次分享,其中談到了 Dubbo Mesh 的整體發展思路是“借力開源、反哺開源”,也講到了 Service Mesh 在阿里巴巴的發路徑將經歷以下三大階段:

  • 撬動
  • 做透價值滲透
  • 實現技術換代

Dubbo Mesh 在閒魚生產環境的落地,分享的是以多語言為撬動點的階段性總結。

文章首發於「QCon」,阿里巴巴中介軟體授權轉載。



Dubbo Mesh 在閒魚生產環境中的落地實踐


閒魚場景的特點

閒魚採用的程式語言是 Dart,思路是通過 Flutter 和 Dart 實現 iOS、Android 兩個客戶端以及 Dart 服務端,以“三端一體”的思路去探索多端融合的高效軟體開發模式。更多細節請參考作者同事陳新新在 2018 QCon 上海站的主題分享《Flutter & Dart 三端一體化開發》。本文將關注三端中的 Dart 服務端上運用 Dubbo Mesh 去解耦 Dubbo RPC 框架的初步實踐成果。

Dart 服務端是一個服務呼叫膠水層,收到來自接入閘道器發來的 HTTP 請求後,通過 C++ SDK 呼叫集團廣泛提供的 Dubbo 服務完成業務邏輯處理後返回結果。然而,C++ SDK 的功能與 Java 的存在一定的差距,比如缺失限流降級等對於保障大促穩定性很重要的功能。從長遠發展的角度,閒魚團隊希望通過 Dubbo Mesh 能遮蔽或簡化不同技術棧使用中介軟體(比如,RPC、限流降級、監控、配置管理等)的複雜性。這一訴求的由來,是閒魚團隊通過幾個月的實踐,發現在 Dart 語言中通過 C++ SDK 逐個接入不同中介軟體存在定製和維護成本高的問題。值得說明,所謂的“定製”是因為 C++ SDK 的能力弱於 Java SDK 而要做補齊所致。

Dart 服務端自身的業務邏輯很輕且在一些場景下需要呼叫 20 多次 Dubbo 服務,這對於 Dubbo Mesh 的技術挑戰會顯得更大。在 Dubbo Mesh 還沒在生產環境落地過而缺乏第一手資料的情形下,其效能是否完全滿足業務的要求是大家普遍關心的。

架構與實現



Dubbo Mesh 在閒魚生產環境中的落地實踐



Dubbo Mesh 架構圖(監控部分未表達)

圖中的虛框代表了一個Pouch容器(也可以是一臺物理機或虛擬機器)。左邊兩個容器部署了 Dubbo Mesh,剩下最右邊的則沒有。目前 Dubbo Mesh 主要包含 Bonder、Pilot、Envoy 三個程式,以及被輕量化的 Thin SDK。其中:

  • Envoy 承擔了資料平面的角色,所有 mesh 流量將由它完成服務發現與路由而中轉。Envoy 由 Lyft 初創且目前成為了 CNCF 的畢業專案,我們在之上增加了對 Dubbo 協議的支援,並將之反哺到了開源社群(還有不少程式碼在等待社群 review 通過後才能進到 GitHub 的程式碼倉庫)。
  • Pilot 和 Bonder 共同承擔控制平面的角色,實現服務註冊、程式拉起與保活、叢集資訊和配置推送等功能。Pilot 程式的程式碼源於開源 Istio 的 pilot-discovery 元件,我們針對阿里巴巴集團環境做了一定的改造(比如,與Nacos進行適配去訪問服務註冊中心),且採用下沉到應用機器的方式進行部署,這一點與開源的叢集化部署很不一樣。背後的思考是,Pilot 的叢集化部署對於大規模叢集資訊的同步是非常大的一個挑戰,今天開源的 Istio 並不具備這一能力,未來需要 Nacos 團隊對之進行增強,在沒有完全準備好前通過下沉部署的方式能加速 Service Mesh 的探索歷程。
  • Thin SDK 是 Fat SDK 經過裁剪後只保留了對 Dubbo 協議進行編解碼的能力。為了容災,當 Thin SDK 位於 Consumer 側時增加了一條容災通道,細節將在文後做進一步展開。

資料鏈路全部採用單條 TCP 長連線,這一點與非 mesh 場景是一致的。Pilot 與 Envoy 兩程式間採用的是 gRPC/xDS 協議進行通訊。

圖中同時示例了 mesh 下的 Consumer 能同時呼叫 mesh 下的服務(圖中以 www.mesh.com 域名做示例)和非 mesh 下的服務(圖中以 www.non-mesh.com 域名做示例)。閒魚落地的場景為了避免對 20 多個依賴服務進行改造,流量走的是 mesh 下的 Consumer 呼叫非 mesh 下的 Provider 這一形式,讀者可以理解為圖中最左邊的容器部署的是 Dart 服務端,它將呼叫圖中最右邊容器所提供的服務去實現業務邏輯。

容災

從 Dubbo Mesh 下的 Provider 角度,由於通常是叢集化部署的,當一個 Provider 出現問題(無論是 mesh 元件引起的,還是 Provider 自身導致的)而使服務無法調通時,Consumer 側的 Envoy 所實現的重試機制會將服務請求轉發到其他 Provider。換句話說,叢集化部署的 Provider 天然具備一定的容災能力,在 mesh 場景下無需特別處理。

站在 Dubbo Mesh 的 Consumer 立場,如果完全依賴 mesh 鏈路去呼叫 Provider,當 mesh 鏈路出現問題時則會導致所有服務都調不通,這往往會引發業務可用性問題。為此,Thin SDK 中提供了一個直連 Provider 的機制,只不過實現方式比 Fat SDK 輕量了許多。Thin SDK 會定期從 Envoy 的 Admin 介面獲取所依賴服務的 Provider 的 IP 列表,以備檢測到 mesh 鏈路存在問題時用於直連。比如,針對每一個依賴的服務獲取最多 10 個 Provider 的 IP 地址,當 mesh 鏈路不通時以 round robin 演算法向這些 Provider 直接發起呼叫。由於容災是針對 mesh 鏈路的短暫失敗而準備的,所以 IP 地址的多少並不是一個非常關鍵的點。Thin SDK 檢測 mesh 鏈路的異常大致有如下場景:

  • 與 Envoy 的長連線出現中斷,這是 Envoy 發生 crash 所致。
  • 所發起的服務呼叫收到 No Route Found、No Healthy Upstream 等錯誤響應。

優化

在閒魚落地 Dubbo Mesh 的初期我們走了一個“彎路”。

具體說來,最開始為了快速落地而採用了 Dubbo over HTTP 1.1/2 的模式,也即,將 Dubbo 協議封裝在 HTTP 1.1/2 的訊息體中完成服務呼叫。這一方案雖然能很好地享受 Envoy 已完整支援 HTTP 1.1/2 協議而帶來的開發工作量少的好處,但效能測試表明其資源開銷並不符合大家的預期。體現於,不僅 Consumer 側使用 mesh 後帶來更高的 CPU 開銷,Provider 側也因為要提供通過 HTTP 1.1/2 進行呼叫的能力而導致多出 20% 的 CPU 開銷且存在改造工作。最終,我們回到讓 Envoy 原生支援 Dubbo 協議的道路上來。

Envoy 支援 Dubbo 協議經歷了兩大階段。第一個階段 Envoy 與上游的通訊並沒有採用單條長連線,使得 Provider 的 CPU 開銷因為多連線而存在不可忽視的遞增。第二個階段則完全採用單條長連線,通過多路複用的模式去除了前一階段給 Provider 所帶去的額外 CPU 開銷。

Dubbo Mesh 在閒魚預發環境上線進行效能與功能驗證時,我們意外地發現,Istio 原生 Pilot 的實現會將全量叢集資訊都推送給處於 Consumer 側的 Envoy(Provider 側沒有這一問題),導致 Pilot 自身的 CPU 開銷過大,而頻繁的全量叢集資訊推送也使得 Envoy 不時會出現 CPU 負荷毛刺並遭受沒有必要的記憶體開銷。為此,我們針對這一問題做了叢集資訊按需載入的重大改造,這一優化對於更大規模與範圍下運用 Dubbo Mesh 具有非常重要的意義。優化的大致思路是:

  • Thin SDK 提供一個 API 供 Consumer 的應用在初始化時呼叫,周知其所需呼叫的服務列表。
  • Thin SDK 通過 HTTP API 將所依賴的服務列表告訴 Bonder,Bonder 將之儲存到本地檔案。
  • Envoy 啟動時讀取 Bonder 所儲存的服務列表檔案,將之當作元資訊轉給 Pilot。
  • Pilot 向 Nacos 只訂閱服務列表中的叢集資訊更新訊息且只將這些訊息推送給 Envoy。

監控

可觀測性(observability)是 Service Mesh 非常重要的內容,在服務呼叫鏈路上插入了 Envoy 的情形下,愈加需要通過更強的監控措施去治理其上的所有微服務。Dubbo Mesh 的監控方案並沒有使用 Istio/Mixer 這樣的設計,而是沿用了阿里巴巴集團內部的方式,即資訊由各程式以日誌的形式輸出,然後通過日誌採集程式將之送到指定的服務端進行後期加工並最終展示於控制檯。目前 Dubbo Mesh 通過 EagleEye 去跟蹤呼叫鏈,通過ARMS去展示其他的監控資訊。

效能評估

為了評估 Dubbo Mesh 的效能能否滿足閒魚業務的需要,我們設計瞭如下圖所示的效能比對測試方案。



Dubbo Mesh 在閒魚生產環境中的落地實踐


其中:

  • 測試機器是阿里巴巴集團生產環境中的 3 臺 4 核 8G 記憶體的 Pouch 容器。
  • 藍色方框代表的是程式。測試資料全部從部署了 DartServer 和 Envoy 兩程式的測試機 2 上獲得。
  • 效能資料分別在非 mesh(圖中紅色資料流)和 mesh(圖中藍色資料流)兩個場景下獲得。顯然,Mesh 場景下的服務流量多了 Envoy 程式所帶來的一跳。
  • DartServer 收到來自施壓的 Loader 程式所發來的一個請求後,將發出 21 次到 Provider 程式的 RPC 呼叫。在評估 Dubbo Mesh 的效能時,這 21 次是序列發出的(下文列出的測試資料是在這一情形下收集的),實際閒魚生產環境上線時考慮了進行並行傳送去進一步降低整體呼叫時延(即便沒有 mesh 時,閒魚的業務也是這樣實現的)。
  • Provider 程式端並沒有部署 Envoy 程式。這省去了初期引入 Dubbo Mesh 對 Provider 端的改造成本,降低了落地的工作量和難度。

設計測試方案時,我們與閒魚的同學共創瞭如何回答打算運用 Dubbo Mesh 的業務方一定會問的問題,即“使用 Dubbo Mesh 後對 RT(Response Time)和 CPU 負荷的影響有多大”。背後的動機是,業務方希望通過 RT 這一指標去了解 Dubbo Mesh 對使用者體驗的影響,基於 CPU 負荷的增長去掌握運用新技術所引發的成本。

面對這一問題通常的回答是“在某某 QPS 下,RT 增加了 x%,CPU 負荷增加了 y%”,但這樣的回答如果不進行具體測試是無法給出的(會出現“雞和蛋的問題”)。因為每個業務的天然不同使得一個完整請求的 RT 會存在很大的差別(從幾毫秒到幾百毫秒),而實現業務邏輯所需的計算量又最終決定了機器的 CPU 負荷水平。基於此,我們設計的測試方案在於評估引入 Dubbo Mesh 後,每經過一跳 Envoy 所引入的 RT 和 CPU 增量。當這一資料出來後,業務方完全可以基於自己業務的現有資料去計算出引入 Dubbo Mesh 後的而掌握大致的影響情況。

顯然,背後的邏輯假設是“Envoy 對於每個 Dubbo 服務呼叫的計算量是一樣的”,事實也確實如此。

測試資料

以下是 Loader 發出的請求在併發度為 100 的情形下所採集的資料。



Dubbo Mesh 在閒魚生產環境中的落地實踐


表中:

  • Envoy 的 QPS 是 Loader 的 21 倍,原因在上面測試方案部分有交代。
  • “單跳”的資料是從“21 跳合計”直接除以 21 所得,其嚴謹性值得商榷,但用於初步評估仍具參考價值(有資料比沒有資料強)。
  • “整機負荷”代表了在 mesh 場景下測試機器 2 上 DartServer 和 Envoy 兩程式的 CPU 開銷總和。測試表明,CPU 負荷高時 Envoy 帶來的單跳 RT 增幅更大(比如表中 Loader 的 QPS 是 480 時)。給出整機負荷是為了提醒讀者關注引入 mesh 前業務的正常單機水位,以便更為客觀地評估運用 Dubbo Mesh 將帶來的潛在影響。
  • “CPU 負荷增幅”是指 CPU 增加的幅度。由於測試機是 4 核的,所以整機的 CPU 負荷是 400。

從表中資料來看,隨著機器整體負荷的增加“CPU 負荷增幅”在高段存在波動,這與 RT 在高段的持續增大存在相關,從 RT 在整體測試中完全符合線性增長來看整體資料合理。當然, 後面值得深入研究資料背後的隱藏技術細節以便深入優化。

線上資料

Dubbo Mesh 正式生產環境上線後,我們通過對上線前後的某介面的 RT 資料進行了全天的比對,以便大致掌握 mesh 化後的影響。2019-01-14 該介面全面切成了走 Dubbo Mesh,我們取的是 2019-01-20 日的資料。



Dubbo Mesh 在閒魚生產環境中的落地實踐


圖中藍色是 mesh 化後的 RT 表現(RT 均值 3.3),而橙色是 mesh 化前的 RT 表現(RT 均值 3.27,取的是 2019-01-13 的資料)。由於線上每天的環境都有所不同,要做絕對的比較並不可能。但通過上面的比較不難看出,mesh 化前後對於整體 RT 的影響相當的小。當整體 RT 小於 5 毫秒是如此,如果整體 RT 是幾十、幾百毫秒則影響就更小。

為了幫助更全面地看待業務流量的波動特點,下面分別列出了兩天非 mesh(2019-01-06 和 2019-01-13)和兩天 mesh(2019-01-20 和 2019-01-23)的比對資料。



Dubbo Mesh 在閒魚生產環境中的落地實踐


總之,生產環境上的資料表現與前面效能評估方案下所獲得的測試資料能很好地吻合。

洞見

Dubbo Mesh 在閒魚生產環境的落地實踐讓我們收穫瞭如下的洞見:

  • 服務發現的時效性是 Service Mesh 技術的首要關鍵。 以叢集方式提供服務的情形下(這是分散式應用的常態),因為應用釋出而導致叢集中機器狀態的變更如何及時準確地推送到資料平面是極具挑戰的問題。對於阿里巴巴集團來說,這是 Nacos 團隊致力於解決的問題。開源版本的 Istio 能否在生產環境中運用於大規模分散式應用也首先取決於這一能力。頻繁的叢集資訊推送,將給控制平面和資料平面都帶去負荷擾動,如何通過技術手段控制好擾動是需要特別關注的,對於資料平面來說程式語言的“確定性”(比如,沒有 VM、沒有 GC)在其中將起到不可忽視的作用。
  • 資料平面的軟體實現最大程度地減少記憶體分配與釋放將顯著地改善效能。有兩大舉措可以考慮:
  1. 邏輯與資料相分離。 以在 Envoy 中實現 Dubbo 協議為例,Envoy 每收到一個 RPC 請求都會動態地建立 fitler 去處理,一旦實現邏輯與資料相分離,filter 的建立對於每一個 worker 執行緒有且只有一次,通過這一個 filter 去處理所有的 RPC 請求。
  2. 使用記憶體池。 Envoy 的實現中基本沒有用到記憶體池,如果採用記憶體池對分配出來的各種 bufffer 通過連結串列進行快取,這將省去大量的記憶體分配與釋放而改善效能。再則,對於處理一個 RPC 請求而多次分散分配的動作整合成集中一次性分配也是值得運用的優化技巧。
  • 資料平面的 runtime profiling 是關鍵技術。 Service Mesh 雖然對業務程式碼沒有侵入性,但對服務流量具有侵入性,如何在出現業務毛刺的情形下,快速地通過 runtime profiling 去發現問題或自證清白是非常值得關注的點。

心得

一年不到的探索旅程,讓團隊更加篤定“借力開源,反哺開源”的發展思路。隨著對 Istio 和 Envoy 實現細節的更多掌握,團隊很強列地感受到了走“站在巨人的肩膀上”發展的道路少走了很多彎路,除了快速跟進業界的發展步伐與思路,還將省下精力去做更有價值的事和創新。

此外,Istio 和 Envoy 兩個開源專案的工程質量都很高,單元測試等質量保證手段是日常開發工作中的基礎環節,而我們也完全採納了這些實踐。比如,內部搭建了 CI 環境、每次程式碼提交將自動觸發單元測試、程式碼經過 code review 並完成單元測試才能入庫、自動化效能測試等。

展望

在 2019 年接下來的日子,我們將著手:

  • 與 Sentinel 團隊形成合力,將 Sentinel 的能力納入到 Dubbo Mesh 中補全對 HTTP 和 Dubbo 協議的限流、降級和熔斷能力。
  • 在阿里巴巴集團大範圍 Kubernetes(Sigma 3.1)落地的背景下,與兄弟團隊探索更加優雅的服務流量透明攔截技術方案。
  • 迎合 Serverless 的技術發展趨勢,深化通過 Dubbo Mesh 更好地輕量化應用,以及基於 Dubbo Mesh 對服務流量的天然敏感性去更好地實現 auto-scaling。
  • 在產品的易用性和工程效率方面踏實進取。

未來,我們將及時與讀者分享阿里巴巴集團在 Service Mesh 這一新技術領域的探索成果,也期待與大家有更多的互動交流。


原文連結

本文為雲棲社群原創內容,未經允許不得轉載。


相關文章