作者:互聯網中間件團隊-Zhang Zhenwei
本文為2025年 vivo 開發者大會互聯網技術專場分享內容之一,在微信公眾號《vivo互聯網技術》對話框回覆【2025VDC】獲取 2025VDC 互聯網技術會場議題相關資料。
在Java技術棧場景,vivo主要基於 Apache Dubbo 框架來作為微服務之間的通信橋樑,在內部業務的大規模實踐過程中,我們碰到了質量、性能和容量等方面的挑戰,通過一系列的擴展與優化,較好的解決了相關問題,助力業務更好保障質量,節省算力成本,提升研發效率。
1分鐘看圖掌握核心觀點👇
圖1 VS 圖2,您更傾向於哪張圖來輔助理解全文呢?歡迎在評論區留言。
一、Dubbo 在 vivo 的演進歷程
1.1 vivo 微服務現狀
vivo自2015年通過微服務架構升級以賦能業務增長,通過全網治理,於2018年完成了全網Java技術棧RPC框架統一為Dubbo。 目前,該架構高效支撐了5億用户、覆蓋60+地區的業務體量,實現了萬級微服務在十萬級機器上的穩定運行,日均RPC調用量高達8000億次。
1.2 Dubbo在vivo的演進歷史
Dubbo 是一款 RPC 服務開發框架,主要用於解決微服務架構中的通信與服務治理問題。它提供了服務定義,服務發現、負載均衡、流量管控等豐富能力。vivo在2015年,引入開源社區Dubbo作為Java技術棧RPC框架。而隨業務規模發展,業務側浮現框架版本碎片化現象,產生治理困難,維護成本高等問題。 在19年,vivo引入開源社區2.7.* 版本發佈作為第一個基線版本,對業務側進行了版本收斂。隨後發佈兩個大基線版本,分別為建設三中心分離能力,和應用級註冊發現能力。
1.3 Dubbo執行核心鏈路(概要)
我們先簡要介紹一下Dubbo的整體流程。 流程可分為上下兩部分。上半部分呈現了由提供方、消費方、註冊中心和元數據中心,協同完成的服務註冊與引入。 下半部分為調用流程。Dubbo採用微內核與插件化設計,內部多個抽象層次。
總體而言,一次RPC流程可分為兩類:
- 一是啓動即就緒的靜態過程(如代理生成、服務列表緩存)
- 二是每次調用均需動態計算的部分(如路由、負載均衡、序列化,編解碼),這些常是性能熱點。
二、Dubbo 路由擴展及優化
2.1 Dubbo路由簡介
Dubbo路由是一套基於規則的精細化流量治理組件,其工作流程由服務治理側向Dubbo下發路由策略,從而確保RPC請求能夠被精準的路由至預期的服務實例列表。該機制是支撐灰度發佈、機房容災、環境隔離等流量治理能力的技術基石。 開源版本的Dubbo提供了應用級標籤路由、條件路由和腳本路由等核心路由能力。我們在其基礎上,擴展實現了接口級標籤路由與就近路由兩種增強機制。
2.2 就近路由
2.2.1 就近路由背景説明
一般情況下,同機房內部的網絡調用平均時延在0.1ms左右,而同城多機房間的平均時延在1ms-5ms,跨地域機房之間的網絡時延則更大。 假設內部服務存在大量跨機房調用,尤其針對rt敏感業務,可能因為請求延時的增加,影響服務質量用户體驗。 因此Dubbo就近路由應運而生,其可實現RPC過程優先使用同機房進行調用。 可以看到上圖,提供方在註冊會上報機房信息,消費方調用經過就近路由,只匹配同機房的提供方節點列表。
2.2.2 就近路由場景分析
我們的理想方案如上方所示,是多機房共享註冊中心,流量在就近路由的干涉下,在同機房內流轉。 但此方案面臨下方兩個問題: 存在部分業務單機房部署現象,若強制進行同機房調用,會造成消費方無可用提供者。 同時存在多機房非均勻部署現象,若機房間部署規模差異較大,同機房調用可能造成小規模部署機房的業務集羣雪崩。
2.2.3 就近路由實踐
為解決剛才的問題,我們最終實現如下:
- 中間件聯合CI/CD側,在提供方服務部署時上報機房信息。
- 消費方調用經過就近路由時會遍歷提供方列表,優先篩選同機房提供方實例。
- 新增閾值判斷,當同機房提供方機器規模低於閾值時,路由會自動降級進行全量訪問。這樣可以有效避免單機房部署的無提供者問題 ,以及降低非均勻部署時的集羣雪崩風險。
用上邊的三個請求舉例,在就近路由階段:
- 請求1 發現同機房提供者部署規模超過閾值,屬於安全調用,直接過濾出01機房節點。
- 請求2 發現同機房無可用提供者,則直接觸發降級規則,返回全量節點。
- 請求3 在就近路由階段,發現同機房雖有可用提供者部署,但規模低於閾值,也直接觸發降級規則,返回全量節點。
綜上,就近路由通過簡單的元數據標記和靈活的閾值規則,實現了流量的自動優化與隔離。其改造過程對業務代碼無侵入,並帶來延遲降低、網絡帶寬成本下降、穩定性提升的巨大收益。
2.3 標籤路由能力説明
接下來是標籤路由,標籤路由是一種在微服務架構中用於實現流量精細控制的服務治理策略。
其核心思想是通過控制面為服務實例打上自定義標籤,標籤路由根據消費方調用時指定的標籤,將請求流量路由到匹配這些標籤的提供方實例。 在Dubbo語義中,Dubbo標籤分為動態標籤與靜態標籤:如圖所示,我們用通過配置中心下發動態標籤,標記gray1包含a節點,標記gray2包含c節點,用於標識兩個灰度環境。 而提供方部署時可以自帶靜態標籤,靜態標籤隨Dubbo註冊發現流程被消費方在內存緩存。
以三個請求舉例:
- 請求1 指定了gray1,標籤路由會遍歷提供方列表與gray1對應的列表進行交集計算,最後過濾a節點。
- 請求2 指定gray3,標籤路由發現無可用節點,則請求會降級到無標籤的機器,最後過濾b,d,e節點。
- 請求3 未指定標籤,説明是基線環境調用,標籤路由會篩選未打標籤的機器,最後過濾b,d,e節點。
2.4 我們發現的性能問題
在vivo大規模 Dubbo 提供方集羣場景下,高峯期該業務消費方側應用的整體 CPU 利用率約為60%,而其中負載均衡模塊及路由模塊的 CPU 佔用率竟超過了30%! 通過火焰圖分析可以觀察到這些問題存在共性 : 相關方法均涉及遍歷操作,其時間複雜度與提供方節點數量成正相關。在大規模集羣部署環境下,路由與負載均衡模塊因遍歷計算產生了明顯的資源消耗 。
2.4.1 路由優化實踐--減少遍歷運算
優化思路
- 降低消費方側遍歷次數:我們發現部分業務是完全不使用應用級標籤路由的,而為了支持靜態標籤場景,應用級路由對於不帶標籤的請求,還是需要全量遍歷,以篩選無靜態標籤的節點。這部分無效遍歷會造成算力空轉浪費。因此我們第一個優化是對此類業務關閉了應用級路由。
- 根據火焰圖我們瞭解到,在負載均衡中,負載均衡器需要全量遍歷節點以獲取權重。那麼這時我們可以試圖降低參與負載均衡計算的節點數:在負載均衡前,我們新增了虛擬分組。當路由篩選後的實例規模超過閾值後,虛擬分組模塊會將實例列表拆分成多個小規模分組,通過對分組隨機選擇,倍數級降低了進入負載均衡的節點數,降低了負載均衡遍歷次數。
2.4.2 路由優化實踐
1.引入位圖緩存
由火焰圖現象發現,無論是就近路由,還是標籤路由,篩選流程以及交集計算流程依然存在大量遍歷操作帶來算力損耗。首先引入緩存減少遍歷。
對於標籤路由,可以對提供者節點做如下分類: 帶動態標籤的節點,帶靜態標籤的節點,未打標籤的節點,我們可以提前在建立路由元數據的時候,對不同種類節點進行緩存。
我們在標籤路由內設置了緩存單元,對上述三類節點,進行了分類緩存。 類似的,在就近路由內,對不同機房的提供者列表,直接進行緩存。 同時,我們以位圖形式組織了緩存。
以圖中請求為例,全量節點為a,b,c至j,10個節點。 在應用級標籤路由中共維護四份緩存:有gray1,gray2,靜態標籤位圖,無標籤位圖。類似的,接口級維護兩份,分別為grayA標籤位圖,與無標籤位圖。最後是就近路由,維護機房級別的位圖緩存。
請求一從loc1機房發起攜帶應用級標籤gray1,接口級標籤garyA。經歷應用標籤路由與運算,可用列表為a,b,g,經過接口級路由與運算,依然a,b,g。經過就近路由與運算後,只保留ab。由此我們完成了路由執行復雜度從O(n) -> O(1)的挑戰。
2.緩存一致性
我們在路由加入epoch戳,用於緩存版本比對。 消費方發起請求時,會攜帶最新已經以位圖形式儲存好的提供方列表,以及對應的epoch戳。每到新一級路由時,新路由會比對自身緩存epoch戳與初始epoch是否一致。 如果一致,則證明視圖是一致的,直接使用自身位圖緩存與上一階段的位圖結果進行與位運算。 如果不一致,則證明當前路由緩存待更新,那麼會直接實時用最新路由配置規則與上一階段的計算的位圖結果進行遍歷計算。
3.主動緩存更新策略
在提供方持續發佈過程中,消費方持續進行服務引入,服務字典側會同步刷新最新的Invoker列表,並計算新的epoch戳,並將最新的invoker列表更新通知至路由器,用於提前建立最新緩存,同時路由器更新與服務字典一致的epoch。
2.5 路由優化總結
- vivo通過建設就近路由能力,顯著降低了RT敏感性業務的請求延遲,同時增強了業務的可用性與多機房容災能力。
- 針對路由鏈,我們從兩大方向進行了系統性優化:
- 精簡鏈路,並新增虛擬分組,減少遍歷的算力消耗。
- 引入位圖緩存結構,大幅加速路由交集計算速度,依託主動緩存更新與 epoch 版本比對機制,保證了緩存視圖的強一致性。
優化效果: 隨着服務提供方規模不斷擴大,CPU 使用率和 TPS 性能提升效果愈發顯著。在兩千節點規模下,TPS 提升超100%,CPU 利用率也降低27%
三、Dubbo負載均衡擴展及優化
在一次RPC調用經過路由篩選後,消費者端必須從多個服務實例中,選擇一個節點來發起請求。這個選擇策略,可能直接影響了系統的吞吐量、響應延遲、資源利用率等核心指標。 而Dubbo的負載均衡器,正是承擔這一關鍵決策的核心組件。
3.1 Dubbo負載均衡優化背景介紹
在vivo的互聯網業務高速發展過程中,由於持續引入了不同年份、不同供應商的服務器,並考慮到摩爾定律的影響,這些服務器之間存在顯著的算力差異。 儘管各實例接收的流量基本一致,但在業務高峯期,實例間CPU利用率表現出明顯的不平衡現象。 該現象導致業務集羣暴露出若干問題:如整體集羣算力利用不充分,低算力機器因負載過高易引發超時,並且頻繁觸發負載告警被迫人工干預等。
備註: 可以看到Dubbo內置自適應負載策略,它的理念是能夠基於服務端的多個實時指標,動態計算節點負載,並選擇空閒節點進行調用,實現智能化的彈性負載調度。 但是這裏需要説明,vivo建設自適應策略時期較早,同期開源社區自適應策略尚處於提案階段,只存在初始的社區討論版本。後續vivo對於自適應策略能力的分析與增強是基於此原始版本思路的進行的。當前開源社區已經提供了正式版本,與vivo實現和原始實現有較大差異。
3.2 社區討論版自適應負載均衡
3.2.1 社區討論版自適應負載均衡技術方案
原始方案流程由3部分構成。
- 提供方更新自身CPU利用率,每次指標隨RPC結果返回。
- 消費方異步計算提供方負載,並對負載進行更新並緩存。
- 消費方使用P2C算法,這裏對P2C做一個簡單的介紹:每次負載均衡隨機挑選兩個節點,並直接選擇負載較小的那個節點進行調用。
右側是消費方採用的埋點指標,包括提供方cpu負載,響應時間等參數。 下邊是消費方基於採集指標對負載計算的公式 可以直接簡易理解為消費方計算的提供方實例負載值,與實例CPU負載值,在途請求數,RT,呈正相關;與請求成功率,權重配置值呈負相關。
3.2.2 社區討論版自適應負載均衡壓測結果
從結果可以看出,自適應策略使不同算力的機器在流量承載上出現清晰分層,體現了算法基於節點負載進行動態流量調度的有效性。 但也觀察到,該版本中CPU利用率存在明顯波動,此行為可能會引入服務質量風險。初步分析,利用率震盪原因可能是流量調整機制尚未實現平滑過渡。
3.2.3 社區討論版波動原因分析
以最簡單的雙節點場景為例:假設存在節點 P1 和 P2。 初始階段,P2 負載較高。在提供方將更新的負載指標返回之前,消費方持續將請求集中發往負載較低的 P1,導致 P1 的負載迅速升至峯值,而 P2 此時無調用,負載掉入極低水平。 隨後,消費方更新了負載數據,兩個節點的負載視圖狀態發生反轉,以此循環往復。
基於上述分析,我們可以從兩個關鍵方向着手優化:
- 改進 P2C 的流量分配機制,避免節點在短時間內被集中訪問;
- 是增強指標平滑流轉能力,抑制短期抖動帶來的決策干擾,從而提升系統的整體穩定性。
3.3 vivo版自適應負載均衡優化
- 對P2C的流量分發策略進行調整:原有的機制是一種“贏者通吃”的模式:算法直接將流量全部導向了當前負載最低的節點。 P2C過程會根據節點的負載計算概率係數,負載低的節點被選中的概率更高,但負載高的節點也不會像之前一樣被被完全忽略。這一改進不僅從根源上消除了所有流量瞬間涌向同一節點所帶來的震盪風險,也顯著提升了集羣資源的整體利用率,實現了P2C節點間負載的“削峯填谷”。
- 將負載計算與負載調度分離。如圖所示,負載均衡引入了一個獨立的權重計算單元。該單元的核心目標是維護一個穩定的、全局的、流量調度視圖。 它的設計思路是:消費方根據負載值,計算一個平滑的,連貫變化的虛擬權重,從而間接通過此虛擬權重,使得當前的流量分配過程始終保持穩態。
- P2C過程將基於上述虛擬權重進行節點選擇。
3.4 vivo自適應負載均衡壓測效果展示
左側從左至右依次展示了隨機算法、原始版本自適應策略,以及vivo內部優化後的自適應策略,在雙算力配置集羣的壓測環境中的壓測表現: vivo優化版本不僅實現了更優的流量分層,還將各節點CPU利用率收斂至基本一致的水平,並始終保持穩定,使得集羣達成高效、高吞吐且穩定運行的理想狀態。
右側呈現了原始策略與vivo版自適應策略,相對隨機策略在多項核心指標上的表現,包括TPS、平均RT等,可看出此次優化效果顯著,各項指標均有大幅提升。
3.5 vivo自適應負載均衡生產環境使用效果
- 提升服務容量:在同等集羣規模下,新策略可將集羣流量吞吐上限平均提升約15%,有效增強了整體集羣承載能力。
- 提升服務質量:推廣新策略後,算法在高峯期可有效抑制超時率,請求失敗率下降超過50%。
- 實現有效降本:新算法通過更精準的流量調度,顯著節省了CPU資源佔用,預計每年可為業務降低百萬元級別的計算成本。
- 優化運維人力投入:新負載策略大幅減少了低算力機器的利用率告警頻率。業務方無需在流量高峯期間專門進行集羣實例的靜態權重調整,減輕了運維負擔。
四、技術成果
在Dubbo路由層面:我們重點解決兩大痛點:一是路由篩選時的大量遍歷操作導致消費端CPU資源浪費,二是跨機房調用對RT敏感業務的性能影響。
針對路由遍歷效率問題,我們實施4項優化:
- 關閉未使用的路由,減少無效計算;
- 引入位圖緩存機制,將路由篩選複雜度從 O(n) 降至 O(1),大幅提升執行效率;
- 新增分組路由功能,有效減少進入負載均衡模塊的實例數量,減輕後續處理壓力。
- 建設就近路由能力,顯著提升了跨機房調用的業務體驗,提高服務質量。
在負載均衡方面:我們原先面臨着原靜態策略難以適配底層設施差異,導致算力利用率低的問題。為此,我們基於社區版本構建了vivo自適應負載均衡能力,重點優化P2C算法並引入權重計算單元,實現流量自適應與平滑調度,最終顯著提升服務容量與質量,同時實現降本和節約人力的目標。
五、未來展望
最後是vivo對於Dubbo未來在公司內演進的一些計劃與思考:
- 推進Dubbo與開源社區版本對齊: 目前公司內部的Dubbo基線版本基於開源社區2.7.*構建,而社區現已演進至3.*版本,社區3.*版本全面支持新一代高性能通信協議,具備更強的雲原生特性。那麼未來我們也將持續推進vivo內部基線版本升級,引入更多優秀的開源能力與模型,更好地擁抱雲原生架構。
- 構建跨語言的統一微服務治理平台: 當前vivo的服務治理能力主要圍繞Java和C++技術棧,隨着公司業務高速發展,如Go、Python等語言逐漸廣泛應用。目前,跨語言服務調用的需求顯著增長,同時也帶來了技術棧碎片化、治理能力不協同、配置模型不統一、開發認知不一致等挑戰。為此,我們計劃建設一套標準化的、語言無關的微服務治理平台。該平台將提供統一的服務發現、流量監控、流量治理以及服務觀測能力。真正實現“多語言開發,一體化治理”的目標,以降低未來的系統複雜性與運維成本。