Dubbo?LoadBalance基于權(quán)重的隨機(jī)負(fù)載均衡算法提高服務(wù)性能
導(dǎo)讀
Dubbo是一個(gè)分布式服務(wù)框架,能避免單點(diǎn)故障和支持服務(wù)的橫向擴(kuò)容。一個(gè)服務(wù)通常會(huì)部署多個(gè)實(shí)例。如何從多個(gè)服務(wù) Provider 組成的集群中挑選出一個(gè)進(jìn)行調(diào)用,就涉及到一個(gè)負(fù)載均衡的策略。
負(fù)載均衡職責(zé)是將網(wǎng)絡(luò)請求或者其他形式的負(fù)載“均攤”到不同的服務(wù)節(jié)點(diǎn)上,從而避免服務(wù)集群中部分節(jié)點(diǎn)壓力過大、資源緊張,而另一部分節(jié)點(diǎn)比較空閑的情況。通過合理的負(fù)載均衡算法,我們希望可以讓每個(gè)服務(wù)節(jié)點(diǎn)獲取到適合自己處理能力的負(fù)載,實(shí)現(xiàn)處理能力和流量的合理分配。常用的負(fù)載均衡可分為軟件負(fù)載均衡(比如,日常工作中使用的 Nginx)和硬件負(fù)載均衡(主要有 F5、Array、NetScaler 等,不過開發(fā)工程師在實(shí)踐中很少直接接觸到)。
Dubbo提供了 5 種負(fù)載均衡實(shí)現(xiàn)
- 基于 Hash 一致性的 ConsistentHashLoadBalance;
- 基于權(quán)重隨機(jī)算法的 RandomLoadBalance;
- 基于最少活躍調(diào)用數(shù)算法的 LeastActiveLoadBalance;
- 基于加權(quán)輪詢算法的 RoundRobinLoadBalance;
- 基于最短響應(yīng)時(shí)間的 ShortestResponseLoadBalance 。
LoadBalance 是一個(gè)擴(kuò)展接口,默認(rèn)使用的擴(kuò)展實(shí)現(xiàn)是 RandomLoadBalance,其定義如下所示,其中的 @Adaptive 注解參數(shù)為 loadbalance,即動(dòng)態(tài)生成的適配器會(huì)按照 URL 中的 loadbalance 參數(shù)值選擇擴(kuò)展實(shí)現(xiàn)類。
@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {
@Adaptive("loadbalance")
<T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
}LoadBalance 接口中 select() 方法的核心功能是根據(jù)傳入的 URL 和 Invocation,以及自身的負(fù)載均衡算法,從 Invoker 集合中選擇一個(gè) Invoker 返回。
ConsistentHashLoadBalance(一致性 Hash算法)
底層使用一致性 Hash 算法實(shí)現(xiàn)負(fù)載均衡。
一致性 Hash 負(fù)載均衡可以讓參數(shù)相同的請求每次都路由到相同的服務(wù)節(jié)點(diǎn)上,這種負(fù)載均衡策略可以在某些 Provider 節(jié)點(diǎn)下線的時(shí)候,讓這些節(jié)點(diǎn)上的流量平攤到其他 Provider 上,不會(huì)引起流量的劇烈波動(dòng)。
假設(shè)現(xiàn)在有 1、2、3 三個(gè) Provider 節(jié)點(diǎn)對外提供服務(wù),有 100 個(gè)請求同時(shí)到達(dá),如果想讓請求盡可能均勻地分布到這三個(gè) Provider 節(jié)點(diǎn)上,我們可能想到的最簡單的方法就是 Hash 取模,即 hash(請求參數(shù)) % 3。如果參與 Hash 計(jì)算的是請求的全部參數(shù),那么參數(shù)相同的請求將會(huì)落到同一個(gè) Provider 節(jié)點(diǎn)上。不過此時(shí)如果突然有一個(gè) Provider 節(jié)點(diǎn)出現(xiàn)宕機(jī)的情況,那我們就需要對 2 取模,即請求會(huì)重新分配到相應(yīng)的 Provider 之上。在極端情況下,甚至?xí)霈F(xiàn)所有請求的處理節(jié)點(diǎn)都發(fā)生了變化,這就會(huì)造成比較大的波動(dòng)。
為了避免因一個(gè) Provider 節(jié)點(diǎn)宕機(jī),而導(dǎo)致大量請求的處理節(jié)點(diǎn)發(fā)生變化的情況,我們可以考慮使用一致性 Hash 算法。一致性 Hash 算法的原理也是取模算法,與 Hash 取模的不同之處在于:Hash 取模是對 Provider 節(jié)點(diǎn)數(shù)量取模,而一致性 Hash 算法是對 2^32 取模。
一致性 Hash 算法需要同時(shí)對 Provider 地址以及請求參數(shù)進(jìn)行取模:
hash(Provider地址) % 2^32
hash(請求參數(shù)) % 2^32
Provider 地址和請求經(jīng)過對 2^32 取模得到的結(jié)果值,都會(huì)落到一個(gè) Hash 環(huán)上.
一致性 Hash節(jié)點(diǎn)均勻分布
我們按順時(shí)針的方向,依次將請求分發(fā)到對應(yīng)的 Provider。這樣,當(dāng)某臺(tái) Provider 節(jié)點(diǎn)宕機(jī)或增加新的 Provider 節(jié)點(diǎn)時(shí),只會(huì)影響這個(gè) Provider 節(jié)點(diǎn)對應(yīng)的請求。
在理想情況下,一致性 Hash 算法會(huì)將這三個(gè) Provider 節(jié)點(diǎn)均勻地分布到 Hash 環(huán)上,請求也可以均勻地分發(fā)給這三個(gè) Provider 節(jié)點(diǎn)。但在實(shí)際情況中,這三個(gè) Provider 節(jié)點(diǎn)地址取模之后的值,可能差距不大,這樣會(huì)導(dǎo)致大量的請求落到一個(gè) Provider 節(jié)點(diǎn)上。就出現(xiàn)了數(shù)據(jù)傾斜的問題。所謂數(shù)據(jù)傾斜是指由于節(jié)點(diǎn)不夠分散,導(dǎo)致大量請求落到了同一個(gè)節(jié)點(diǎn)上,而其他節(jié)點(diǎn)只會(huì)接收到少量請求的情況。
為了解決一致性 Hash 算法中出現(xiàn)的數(shù)據(jù)傾斜問題,又演化出了 Hash 槽的概念。
Hash 槽解決數(shù)據(jù)傾斜的思路是:既然問題是由 Provider 節(jié)點(diǎn)在 Hash 環(huán)上分布不均勻造成的,那么可以虛擬出 n 組 P1、P2、P3 的 Provider 節(jié)點(diǎn) ,讓多組 Provider 節(jié)點(diǎn)相對均勻地分布在 Hash 環(huán)上。如下圖所示,相同陰影的節(jié)點(diǎn)均為同一個(gè) Provider 節(jié)點(diǎn),比如 P1-1、P1-2……P1-99 表示的都是 P1 這個(gè) Provider 節(jié)點(diǎn)。引入 Provider 虛擬節(jié)點(diǎn)之后,讓 Provider 在圓環(huán)上分散開來,以避免數(shù)據(jù)傾斜問題。
RandomLoadBalance(加權(quán)隨機(jī)算法)
RandomLoadBalance 是一個(gè)簡單、高效的負(fù)載均衡實(shí)現(xiàn),它也是 Dubbo 默認(rèn)使用的 LoadBalance 實(shí)現(xiàn)。
下面我們通過隨機(jī)數(shù)生成器在 [0, 10) 這個(gè)范圍內(nèi)生成一個(gè)隨機(jī)數(shù),然后計(jì)算這個(gè)隨機(jī)數(shù)會(huì)落到哪個(gè)區(qū)間中。例如,隨機(jī)生成 4,就會(huì)落到 Provider A 對應(yīng)的區(qū)間中,此時(shí) RandomLoadBalance 就會(huì)返回 Provider A 這個(gè)節(jié)點(diǎn)。
接下來我們再來看 RandomLoadBalance 中 doSelect() 方法的實(shí)現(xiàn),其核心邏輯為三個(gè)關(guān)鍵點(diǎn):
- 計(jì)算每個(gè) Invoker 對應(yīng)的權(quán)重值以及總權(quán)重值;
- 當(dāng)各個(gè) Invoker 權(quán)重值不相等時(shí),計(jì)算隨機(jī)數(shù)應(yīng)該落在哪個(gè) Invoker 區(qū)間中,返回對應(yīng)的 Invoker 對象;
- 當(dāng)各個(gè) Invoker 權(quán)重值相同時(shí),隨機(jī)返回一個(gè) Invoker 即可。
RandomLoadBalance 經(jīng)過多次請求后,能夠?qū)⒄{(diào)用請求按照權(quán)重值均勻地分配到各個(gè) Provider 節(jié)點(diǎn)上。
LeastActiveLoadBalance(最小活躍數(shù)負(fù)載均衡算法)
LeastActiveLoadBalance 使用的是最小活躍數(shù)負(fù)載均衡算法。它認(rèn)為當(dāng)前活躍請求數(shù)越小的 Provider 節(jié)點(diǎn),剩余的處理能力越多,處理請求的效率也就越高,那么該 Provider 在單位時(shí)間內(nèi)就可以處理更多的請求,所以我們應(yīng)該優(yōu)先將請求分配給該 Provider 節(jié)點(diǎn)。
LeastActiveLoadBalance 需要配合 ActiveLimitFilter 使用,ActiveLimitFilter 會(huì)記錄每個(gè)接口方法的活躍請求數(shù),在 LeastActiveLoadBalance 進(jìn)行負(fù)載均衡時(shí),只會(huì)從活躍請求數(shù)最少的 Invoker 集合里挑選 Invoker。
在 LeastActiveLoadBalance 的實(shí)現(xiàn)中,首先會(huì)選出所有活躍請求數(shù)最小的 Invoker 對象,之后的邏輯與RandomLoadBalance 完全一樣,按照這些 Invoker 對象的權(quán)重挑選最終的Invoker 對象。
RoundRobinLoadBalance(加權(quán)輪詢負(fù)載均衡算法)
輪詢指的是將請求輪流分配給每個(gè) Provider。例如,有 A、B、C 三個(gè) Provider 節(jié)點(diǎn),按照普通輪詢的方式,我們會(huì)將第一個(gè)請求分配給 Provider A,將第二個(gè)請求分配給 Provider B,第三個(gè)請求分配給 Provider C,第四個(gè)請求再次分配給 Provider A……如此循環(huán)往復(fù)。
輪詢是一種無狀態(tài)負(fù)載均衡算法,實(shí)現(xiàn)簡單,適用于集群中所有 Provider 節(jié)點(diǎn)性能相近的場景。 但現(xiàn)實(shí)情況中就很難保證這一點(diǎn)了,因?yàn)楹苋菀壮霈F(xiàn)集群中性能最好和最差的 Provider 節(jié)點(diǎn)處理同樣流量的情況,這就可能導(dǎo)致性能差的 Provider 節(jié)點(diǎn)各方面資源非常緊張,甚至無法及時(shí)響應(yīng)了,但是性能好的 Provider 節(jié)點(diǎn)的各方面資源使用還較為空閑。這時(shí)我們可以通過加權(quán)輪詢的方式,降低分配到性能較差的 Provider 節(jié)點(diǎn)的流量。
加權(quán)之后,分配給每個(gè) Provider 節(jié)點(diǎn)的流量比會(huì)接近或等于它們的權(quán)重比。例如,Provider 節(jié)點(diǎn) A、B、C 權(quán)重比為 5:1:1,那么在 7 次請求中,節(jié)點(diǎn) A 將收到 5 次請求,節(jié)點(diǎn) B 會(huì)收到 1 次請求,節(jié)點(diǎn) C 則會(huì)收到 1 次請求。
ShortestResponseLoadBalance(最短響應(yīng)時(shí)間的負(fù)載均衡算法)
ShortestResponseLoadBalance 是Dubbo 2.7 版本之后新增加的一個(gè) LoadBalance 實(shí)現(xiàn)類。它實(shí)現(xiàn)了最短響應(yīng)時(shí)間的負(fù)載均衡算法,也就是從多個(gè) Provider 節(jié)點(diǎn)中選出調(diào)用成功的且響應(yīng)時(shí)間最短的 Provider 節(jié)點(diǎn),不過滿足該條件的 Provider 節(jié)點(diǎn)可能有多個(gè),所以還要再使用隨機(jī)算法進(jìn)行一次選擇,得到最終要調(diào)用的 Provider 節(jié)點(diǎn)。
以上就是Dubbo的負(fù)載均衡(LoadBalance)原理的詳細(xì)內(nèi)容,更多關(guān)于Dubbo 負(fù)載均衡的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
spring-cloud-gateway降級(jí)的實(shí)現(xiàn)
這篇文章主要介紹了spring-cloud-gateway降級(jí)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
Java實(shí)現(xiàn)Dijkstra輸出最短路徑的實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)Dijkstra輸出最短路徑的實(shí)例的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-09-09
SpringBoot3 整合Docker-Compose的實(shí)現(xiàn)步驟
本文主要介紹了SpringBoot3 整合Docker-Compose的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-08-08
mybatis-plus鏈?zhǔn)秸{(diào)用的實(shí)現(xiàn)示例
MyBatis-Plus如何實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用?通過源碼解析,我們發(fā)現(xiàn)它利用泛型和自引用的抽象類來實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,父類定義了一個(gè)自引用的泛型,子類繼承并重寫方法,即可實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,同時(shí)也定義了多種泛型類型,需要的朋友可以參考下2026-02-02
Java使用BigDecimal進(jìn)行運(yùn)算封裝的實(shí)際案例
今天小編就為大家分享一篇關(guān)于Java使用BigDecimal進(jìn)行運(yùn)算封裝的實(shí)際案例,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12
淺談利用Spring的AbstractRoutingDataSource解決多數(shù)據(jù)源的問題
本篇文章主要介紹了淺談利用Spring的AbstractRoutingDataSource解決多數(shù)據(jù)源的問題,具有一定的參考價(jià)值,有需要的可以了解一下2017-08-08
IDEA?報(bào)Plugin'maven-resources-plugin:'not?found?
如果在使用?IDEA?時(shí)遇到?"Plugin?'maven-resources-plugin:'?not?found"?錯(cuò)誤,可能是由于?Maven?倉庫中未找到所需的?Maven?插件,近小編給大家分享幾種解決方法,感興趣的朋友跟隨小編一起看看吧2023-07-07
SpringBoot結(jié)合ProGuard實(shí)現(xiàn)代碼混淆(最新版)
這篇文章主要介紹了SpringBoot結(jié)合ProGuard實(shí)現(xiàn)代碼混淆(最新版),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10

