前言

負載均衡是指在集羣中,將多個數據請求分散在不同單元上進行執行,主要爲了提高系統容錯能力和加強系統對數據的處理能力。

在 Dubbo 中,一次服務的調用就是對所有實體域 Invoker 的一次篩選過濾,最終選定具體調用的 Invoker。首先在 Directory 中獲取全部 Invoker 列表,通過路由篩選出符合規則的 Invoker,最後再經過負載均衡選出具體的 Invoker。所以 Dubbo 負載均衡機制是決定一次服務調用使用哪個提供者的服務。

整體結構

Dubbo 負載均衡的分析入口是 org.apache.dubbo.rpc.cluster.loadbalance.AbstractLoadBalance 抽象類,查看這個類繼承關係。

這個被 RandomLoadBalance、LeastActiveLoadBalance、RoundRobinLoadBalance 及 ConsistentHashLoadBalance 類繼承,這四個類是 Dubbo 中提供的四種負載均衡算法的實現。

名稱 說明
RandomLoadBalance 隨機算法,根據權重設置隨機的概率
LeastActiveLoadBalance

最少活躍數算法,指請求數和完成數之差 使 行效率高的服務 接收更多請求

RoundRobinLoadBalance 加權輪訓算法,根據權重設置輪訓比例
ConsistentHashLoadBalance

Hash 一致性算法,相同請求參數分配 相同提供者

以上則是 Dubbo 提供的四種負載均衡算法。

從上圖中,看到 AbstractLoadBalance 實現了 LoadBalance 接口,同時是一個 SPI 接口,指定默認實現爲 RandomLoadBalance 隨機算法機制。

抽象類 AbstractLoadBalance 中,實現了負載均衡通用的邏輯,同時給子類聲明瞭一個抽象方法供子類實現其負載均衡的邏輯。

在 AbstractLoadBalance 中,getWeight 和 calculateWarmupWeight 方法是獲取和計算當前 Invoker 的權重值。

getWeight 中獲取當前權重值,通過 URL 獲取當前 Invoker 設置的權重,如果當前服務提供者啓動時間小於預熱時間,則會重新計算權重值,對服務進行降權處理,保證服務能在啓動初期不分發設置比例的全部流量,健康運行下去。

calculateWarmupWeight 是重新計算權重值的方法,計算公式爲: 服務運行時長/(預熱時長/設置的權重值) ,等價於  (服務運行時長/預熱時長)*設置的權重值 ,同時條件  服務運行時長<預熱時長 。由該公式可知,預熱時長和設置的權重值不變,服務運行時間越長,計算出的值越接近 weight,但不會等於 weight。在返回計算後的權重結果中,對小於1和大於設置的權重值進行了處理,當重新計算後的權重小於1時返回1;處於1和設置的權重值之間時,直接返回計算後的結果;當權重大於設置的權重值時(因爲條件限制,不會出現該類情況),返回設置的權重值。所以得出結論: 重新計算後的權重值爲 1 ~ 設置的權重值,運行時間越長,計算出的權重值越接近設置的權重值

配置方式

服務端

通過 XML 配置方式:

通過 Properties 配置:

通過註解方式:

客戶端

通過 XML 配置方式:

通過 Properties 配置:

通過註解配置方式:

實現方式也可通過 Dubbo-Admin 管理後臺進行配置,如圖:

隨機算法

加權隨機算法負載均衡策略(RandomLoadBalance)是 dubbo 負載均衡的默認實現方式,根據權重分配各個 Invoker 隨機選中的比例。這裏的意思是:將到達負載均衡流程的 Invoker 列表中的 權重進行求和,然後求出單個 Invoker 權重在總權重中的佔比,隨機數就在總權重值的範圍內生成。

如圖,假如當前有 192.168.1.10 和  192.168.1.11 兩個負載均衡的服務,權重分別爲 4、6 ,則它們的被選中的比例爲 2/5、3/5。

當生成隨機數爲 6 時,就會選中 192.168.1.11 的服務。

dubbo 中 RandomLoadBalance 的 doSelect 實現代碼:

以上就是加權隨機策略的實現,這裏比較主要關注計算生成的隨機數對應的 Invoker。通過遍歷權重數組,生成的數累減當前權重值,當 offset 爲 0 時,就表示 offset 對應當前的 Invoker 服務。

以生成的隨機數爲 6 爲例,遍歷 Invokers 長度:

  1. 第一輪:offset = 6 - 4 = 2 不滿足 offset < 0,繼續遍歷。

  2. 第二輪:offset = 2 - 6 = -4 滿足 offset < 0,返回當前索引對應的 Invoker。因爲 offset 返回負數,表示 offset 落在當前 Invoker 權重的區間裏。

加權隨機策略並非一定按照比例被選到,理論上調用次數越多,分佈的比例越接近權重所佔的比例。

最少活躍數算法

最小活躍數負載均衡策略(LeastActiveLoadBalance)是從最小活躍數的 Invoker 中進行選擇。什麼是活躍數呢?活躍數是一個 Invoker 正在處理的請求的數量,當 Invoker 開始處理請求時,會將活躍數加 1,完成請求處理後,將相應 Invoker 的活躍數減 1。找出最小活躍數後,最後根據權重進行選擇最終的 Invoker。如果最後找出的最小活躍數相同,則隨機從中選中一個 Invoker。

這段代碼的整個邏輯就是,從 Invokers 列表中篩選出最小活躍數的 Invoker,然後類似加權隨機算法策略方式選擇最終的 Invoker 服務。

輪詢算法

加權輪詢負載均衡策略(RoundRobinLoadBalance)是基於權重來決定輪詢的比例。普通輪詢會將請求均勻的分佈在每個節點,但不能很好調節不同性能服務器的請求處理,所以加權負載均衡來根據權重在輪詢機制中分配相對應的請求比例給每臺服務器。

上面選中 Invoker 邏輯爲:每個 Invoker 都有一個 current 值,初始值爲自身權重。在每個 Invoker 中 current=current+weight 。遍歷完 Invoker 後,current 最大的那個 Invoker 就是本次選中的 Invoker。選中 Invoker 後,將本次 current 值計算  current=current-totalWeight

以上面 192.168.1.10 和  192.168.1.11 兩個負載均衡的服務,權重分別爲 4、6 。基於選中前  current=current+weight 、選中後  current=current-totalWeight 計算公式得出如下

請求次數 選中前 current 選中後 current 被選中服務
1 [4, 6] [4, -4] 192.168.1.11
2 [8, 2] [-2, 2] 192.168.1.10
3 [2, 8] [2, -2] 192.168.1.11
4 [6, 4] [-4, 4] 192.168.1.10
5 [0, 10] [0, 0] 192.168.1.11

一致性 Hash 算法

一致性 Hash 負載均衡策略(ConsistentHashLoadBalance)是讓參數相同的請求分配到同一機器上。把每個服務節點分佈在一個環上,請求也分佈在環形中。以請求在環上的位置,順時針尋找換上第一個服務節點。如圖所示:

同時,爲避免請求散列不均勻,dubbo 中會將每個 Invoker 再虛擬多個節點出來,使得請求調用更加均勻。

一致性 Hash 修改配置如下:

一致性 Hash 實現如下:

doSelect 中主要實現緩存檢查和 Invokers 變動檢查,一致性 hash 負載均衡的實現在這個內部類 ConsistentHashSelector 中實現。

一致 hash 實現過程就是先創建好虛擬節點,虛擬節點保存在 TreeMap 中。TreeMap 的 key 爲配置的參數先進行 md5 運算,然後將 md5 值進行 hash 運算。TreeMap 的 value 爲被選中的 Invoker。

最後請求時,計算參數的 hash 值,去從 TreeMap 中獲取 Invoker。

總結

Dubbo 負載均衡的實現,技巧上還是比較優雅,可以多多學習其編碼思維。在研究其代碼時,需要仔細研究其實現原理,否則比較難懂其思想。

推薦閱讀

《Dubbo 路由機制的實現》

《Dubbo 擴展點加載機制:從 Java SPI 到 Dubbo SPI》

《Dubbo之服務消費原理》

《Dubbo之服務暴露》

《你必須會的 JDK 動態代理和 CGLIB 動態代理》

關注【ytao】,更多原創好文

相關文章