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


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 這一新技術領域的探索成果,也期待與大家有更多的互動交流。

本文作者:至簡,阿里巴巴中間件高級技術專家,是阿里巴巴集團 Service Mesh 方向的重要參與者和推動者。

關於 Dubbo Mesh 的首次公開分享


分享到:


相關文章: