1. 程式人生 > >Dubbo加權輪詢負載均衡的原始碼和Bug,瞭解一下?

Dubbo加權輪詢負載均衡的原始碼和Bug,瞭解一下?

 

 

 

本文是對於Dubbo負載均衡策略之一的加權隨機演算法的詳細分析。從2.6.4版本聊起,該版本在某些情況下存在著比較嚴重的效能問題。由問題入手,層層深入,瞭解該演算法在Dubbo中的演變過程,讀懂它的前世今生。

 

之前也寫了Dubbo的負載均衡策略:

《一文講透Dubbo負載均衡之最小活躍數演算法》

《Dubbo一致性雜湊負載均衡的原始碼和Bug,瞭解一下?》

 

本文目錄

第一節:什麼是輪詢?

本小節主要是介紹輪詢演算法和其對應的優缺點。引出加權輪詢演算法。

 

第二節:什麼是加權輪詢?

本小節主要是介紹加權輪詢的概率,並和加權隨機演算法做對比。區分兩者之間的關係。

 

第三節:Dubbo 2.6.4版本的實現

本小節主要分析了Dubbo 2.6.4版本的原始碼,以及對呼叫過程進行了詳細的分析。並引出該版本的效能問題。

 

第四節:推翻,重建

針對Dubbo 2.6.4版本的效能問題,在對應的issue中進行了激烈的討論。並提出了第一版優化意見,時間複雜度優化到了常量級。但不久之後,又有人發現了該版本的其他問題,計算過程不夠平滑。

 

第五節:再推翻,再重建,平滑加權。

針對改進後的演算法還是不夠平滑的問題,最終藉助Nginx的思想,融入了平滑加權的過程,形成最終版。

 

什麼是輪詢?

在描述加權輪詢之前,先解釋一下什麼是輪詢演算法,如下圖所示:

 

假設我們有A、B、C三臺伺服器,共計處理6個請求,服務處理請求的情況如下:

第一個請求傳送給了A伺服器 

第二個請求傳送給了B伺服器

第三個請求傳送給了C伺服器 

第四個請求傳送給了A伺服器 

第五個請求傳送給了B伺服器 

第六個請求傳送給了C伺服器

......

上面這個例子演示的過程就叫做輪詢。可以看出,所謂輪詢就是將請求輪流分配給每臺伺服器。

 

輪詢的優點是無需記錄當前所有伺服器的連結狀態,所以它一種無狀態負載均衡演算法,實現簡單,適用於每臺伺服器效能相近的場景下。

 

輪詢的缺點也是顯而易見的,它的應用場景要求所有伺服器的效能都相同,非常的侷限。

 

大多數實際情況下,伺服器效能是各有差異,針對性能好的伺服器,我們需要讓它承擔更多的請求,即需要給它配上更高的權重。

 

所以加權輪詢,應運而生。

 

什麼是加權輪詢?

為了解決輪詢演算法應用場景的侷限性。當遇到每臺伺服器的效能不一致的情況,我們需要對輪詢過程進行加權,以調控每臺伺服器的負載。

 

經過加權後,每臺伺服器能夠得到的請求數比例,接近或等於他們的權重比。比如伺服器 A、B、C 權重比為 5:3:2。那麼在10次請求中,伺服器 A 將收到其中的5次請求,伺服器 B 會收到其中的3次請求,伺服器 C 則收到其中的2次請求。

 

這裡要和加權隨機演算法做區分哦。加權隨機我在《一文講透Dubbo負載均衡之最小活躍數演算法》中介紹過,直接把畫的圖拿過來:

上面這圖是按照比例畫的,可以直觀的看到,對於某一個請求,區間(權重)越大的伺服器,就越可能會承擔這個請求。所以,當請求足夠多的時候,各個伺服器承擔的請求數,應該就是區間,即權重的比值。

 

假設有A、B、C三臺伺服器,權重之比為5:3:2,一共處理10個請求。

 

那麼負載均衡採用加權隨機演算法時,很有可能A、B服務就處理完了這10個請求,因為它是隨機呼叫。

 

採用負載均衡採用輪詢加權演算法時,A、B、C服務一定是分別承擔5、3、2個請求。

Dubbo2.6.4版本的實現 

對於Dubbo2.6.4版本的實現分析,可以看下圖,我加了很多註釋,其中的輸出語句都是我加的:

 

示例程式碼還是沿用之前文章中的Demo,不瞭解的可以檢視《一文講透Dubbo負載均衡之最小活躍數演算法》,本文分別在20881、20882、20883埠啟動三個服務,各自的權重分別為1,2,3。

 

客戶端呼叫8次:

 

輸出結果如下:

可以看到第七次呼叫後mod=0,回到了第一次呼叫的狀態。形成了一個閉環。

 

再看看判斷的條件是什麼:

其中mod在程式碼中扮演了極其重要的角色,mod根據一個方法的呼叫次數不同而不同,取值範圍是[0,weightSum)。

 

因為weightSum=6,所以列舉mod不同值時,最終的選擇結果和權重變化:

 

可以看到20881,20882,20883承擔的請求數量比值為1:2:3。同時我們可以看出,當 mod >= 1 後,20881埠的服務就不會被選中了,因為它的權重被減為0了。當 mod >= 4 後,20882埠的服務就不會被選中了,因為它的權重被減為0了。

 

結合判斷條件和輸出結果,我們詳細分析一下(下面內容稍微有點繞,如果看不懂,多結合上面的圖片看幾次):

第一次呼叫

mod=0,第一次迴圈就滿足程式碼塊①的條件,直接返回當前迴圈的invoker,即20881埠的服務。此時各埠的權重情況如下:

第二次呼叫

mod=1,需要進入程式碼塊②,對mod進行一次遞減。

第一次迴圈對20881埠的服務權重減一,mod-1=0。

第二次迴圈,mod=0,迴圈物件是20882埠的服務,權重為2,滿足程式碼塊①,返回當前迴圈的20882埠的服務,此時各埠的權重情況如下:

第三次呼叫

mod=2,需要進入程式碼塊②,對mod進行兩次遞減。

第一次迴圈對20881埠的服務權重減一,mod-1=1;

第二次迴圈對20882埠的服務權重減一,mod-1=0;

第三次迴圈時,mod已經為0,當前迴圈的是20883埠的服務,權重為3,滿足程式碼塊①,返回當前迴圈的20883埠的服務,此時各埠的權重情況如下:

第四次呼叫

mod=3,需要進入程式碼塊②,對mod進行三次遞減。

第一次迴圈對20881埠的服務權重減一,從1變為0,mod-1=2;

第二次迴圈對20882埠的服務權重減一,從2變為1,mod-1=1;

第三次迴圈對20883埠的服務權重減一,從3變為2,mod-1=0;

第四次迴圈的是20881埠的服務,此時mod已經為0,但是20881埠的服務的權重已經變為0了,不滿足程式碼塊①和程式碼塊②,進入第五次迴圈。

第五次迴圈的是20882埠的服務,當前權重為1,mod=0,滿足程式碼塊①,返回20882埠的服務,此時各埠的權重情況如下:

第五次呼叫

mod=4,需要進入程式碼塊②,對mod進行四次遞減。

第一次迴圈對20881埠的服務權重減一,從1變為0,mod-1=3;

第二次迴圈對20882埠的服務權重減一,從2變為1,mod-1=2;

第三次迴圈對20883埠的服務權重減一,從3變為2,mod-1=1;

第四次迴圈的是20881埠的服務,此時mod為1,但是20881埠的服務的權重已經變為0了,不滿足程式碼塊②,mod不變,進入第五次迴圈。

第五次迴圈時,mod為1,迴圈物件是20882埠的服務,權重為1,滿足程式碼塊②,權重從1變為0,mod從1變為0,進入第六次迴圈。

第六次迴圈時,mod為0,迴圈物件是20883埠的服務,權重為2,滿足條件①,返回當前20883埠的服務,此時各埠的權重情況如下:

第六次呼叫

第六次呼叫,mod=5,會迴圈九次,最終選擇20883埠的服務,讀者可以自行分析一波,分析出來了,就瞭解的透透的了。

第七次呼叫

第七次呼叫,又回到mod=0的狀態:

 

2.6.4版本的加權輪詢就分析完了,但是事情並沒有這麼簡單。這個版本的加權輪詢是有效能問題的。

 

該問題對應的issue地址如下:

https://github.com/apache/dubbo/issues/2578

問題出現在invoker返回的時機上:

 

擷取issue裡面的一個回答:

 

10分鐘才選出一個invoker,還怎麼玩?

 

有時間可以讀一讀這個issue,裡面各路大神針對該問題進行了激烈的討論,第一種改造方案被接受後,很快就被推翻,被第二種方案代替,可以說優化思路十分值得學習,很精彩,接下來的行文路線就是按照該issue展開的。

 

推翻,重建。

上面的程式碼時間複雜度是O(mod),而第一次修復之後時間複雜度降低到了常量級別。可以說是一次非常優秀的優化,值得我們學習,看一下優化之後的程式碼:

 

其關鍵優化的點是這段程式碼,我加入輸出語句,便於分析。

 

輸出日誌如下:

 

把上面的輸出轉化到表格中去,7次請求的選擇過程如下:

 

該演算法的原理是:

把服務端都放到集合中(invokerToWeightList),然後獲取服務端個數(length),並計算出服務端權重最大的值(maxWeight)。

 

index表示本次請求到來時,處理該請求的服務端下標,初始值為0,取值範圍是[0,length)。

 

currentWeight表示當前排程的權重,初始值為0,取值範圍是[0,maxWeight)。

 

當請求到來時,從index(就是0)開始輪詢服務端集合(invokerToWeightList),如果是一輪迴圈的開始(index=0)時,則對currentWeight進行加一操作(不會超過maxWeight),在迴圈中找出第一個權重大於currentWeight的服務並返回。

 

這裡說的一輪迴圈是指index再次變為0所經歷過的迴圈,這裡可以把index=0看做是一輪迴圈的開始。每一輪迴圈的次數與Invoker的數量有關,Invoker數量通常不會太多,所以我們可以認為上面程式碼的時間複雜度為常數級。

 

從issue上看出,這個演算法最終被merged了。

 

但是很快又被推翻了:

 

這個演算法不夠平滑。什麼意思呢?

 

翻譯一下上面的內容就是:伺服器[A, B, C]對應權重[5, 1, 1]。進行7次負載均衡後,選擇出來的序列為[A, A, A, A, A, B, C]。前5個請求全部都落在了伺服器A上,這將會使伺服器A短時間內接收大量的請求,壓力陡增。而B和C此時無請求,處於空閒狀態。而我們期望的結果是這樣的[A, A, B, A, C, A, A],不同伺服器可以穿插獲取請求。

 

我們設定20881埠的權重為5,20882、20883埠的權重均為1。

 

進行實驗,發現確實如此:可以看到一共進行7次請求,第1次到5次請求都分發給了權重為5的20881埠的服務,前五次請求,20881和20882都處於空閒狀態:

 

轉化為表格如下:

 

從表格的最終結果一欄也可以直觀的看出,七次請求對應的伺服器埠為:

 

分佈確實不夠均勻。

再推翻,再重建,平滑加權。

從issue中可以看到,再次重構的加權演算法的靈感來源是Nginx的平滑加權輪詢負載均衡:

 

看程式碼之前,先介紹其計算過程。

 

假設每個伺服器有兩個權重,一個是配置的weight,不會變化,一個是currentWeight會動態調整,初始值為0。當有新的請求進來時,遍歷伺服器列表,讓它的currentWeight加上自身權重。遍歷完成後,找到最大的currentWeight,並將其減去權重總和,然後返回相應的伺服器即可。

 

 

如果你還是不知道上面的表格是如何算出來的,我再給你詳細的分析一下第1、2個請求的計算過程:

 

第一個請求計算過程如下:

 

第二個請求計算過程如下:

 

後面的請求你就可以自己分析了。

 

從表格的最終結果一欄也可以直觀的看出,七次請求對應的伺服器埠為:

 

可以看到,權重之比同樣是5:1:1,但是最終的請求分發的就比較的"平滑"。對比一下:

 

對於平滑加權演算法,我想多說一句。我覺得這個演算法非常的神奇,我是徹底的明白了它每一步的計算過程,知道它最終會形成一個閉環,但是我想了很久,我還是不知道背後的數學原理是什麼,不明白為什麼會形成一個閉環,非常的神奇。

 

但是我們只要能夠理解我前面所表達的平滑加權輪詢演算法的計算過程,知道其最終會形成閉環,就能理解下面的程式碼。配合程式碼中的註釋食用,效果更佳。以下程式碼以及註釋來源官網:

http://dubbo.apache.org/zh-cn/docs/source_code_guide/loadbalance.html

 

最後說一句

Dubbo官方提供了四種負載均衡演算法,分別是:

ConsistentHashLoadBalance 一致性雜湊演算法   

LeastActiveLoadBalance 最小活躍數演算法   

RandomLoadBalance  加權隨機演算法  

RoundRobinLoadBalance 加權輪詢演算法

對於官方提供的加權隨機演算法,原理十分簡單。所以在《一文講透Dubbo負載均衡之最小活躍數演算法》中也提到過。

本文是Dubbo負載均衡演算法的最後一篇。前兩篇為:

《一文講透Dubbo負載均衡之最小活躍數演算法》

《Dubbo一致性雜湊負載均衡的原始碼和Bug,瞭解一下?》

至此,Dubbo的負載均衡演算法都已分享完成。

才疏學淺,難免會有紕漏,如果你發現了錯誤的地方,還請你留言給我指出來,我對其加以修改。

感謝您的閱讀,十分歡迎並感謝您的關注。

以上。 

原創不易,歡迎轉發,求個關注,賞個"在看"吧。

相關推薦

Dubbo加權負載均衡原始碼Bug瞭解一下

      本文是對於Dubbo負載均衡策略之一的加權隨機演算法的詳細分析。從2.6.4版本聊起,該版本在某些情況下存在著比較嚴重的效能問題。由問題入手,層層深入,瞭解該演算法在Dubbo中的演變過程,讀懂它的前世今生。   之前也寫了Dubbo的負載均衡策略: 《

學習類似於ribbon機制的負載均衡

ribbon主要的作用就是在本地做負載均衡, 簡單理解為:在註冊中心上根據相同的一個服務名稱,拿到叢集服務的地址, 把叢集的服務地址輪詢, 它的主要原理就是在於,輪詢,就是呼叫不同的地址 所以在底層程式碼實現思路: 1.從註冊中心上面取值, 2.取值之後的個數,次

“鍵””正規化”瞭解一下~

超鍵: 能確定一條資訊的屬性或屬性集合 候選鍵: 最小超鍵,能確定一條資訊的最小屬性集合(可能有幾組,也可能只一組) 主鍵: 從候選鍵中任意定義其中的一組屬性集合,即為主鍵 外來鍵: 兩個關係中,一個關係的主鍵即為另一關係的外來鍵,||該外來鍵需在此關係中做普通屬性||

css背景邊框瞭解一下

css圓角邊框 border-radius 表格border-collapse屬性為separate,表格圓角才能正常顯示 背景屬性 background-color background-image background-repeat

Nginx的負載均衡 - 加權 (Weighted Round Robin)

Nginx版本:1.9.1   演算法介紹   來看一個簡單的Nginx負載均衡配置。 http {     upstream cluster {         server a weight=5;

負載均衡加權演算法

在介紹加權輪詢演算法(WeightedRound-Robin)之前,首先介紹一下輪詢演算法(Round-Robin)。 一:輪詢演算法(Round-Robin) 輪詢演算法是最簡單的一種負載均衡演算法。它的原理是把來自使用者的請求輪流分配給內部的伺服器:從伺服器1開始,直

Nginx的負載均衡加權 (Weighted Round Robin)

負載均衡配置 Nginx 的負載均衡配置如下: http { upstream cluster { server a weight=4; server b weight=2; serve

Nginx學習之十二-負載均衡-加權策略剖析

typedef struct ngx_http_upstream_rr_peers_s ngx_http_upstream_rr_peers_t; struct ngx_http_upstream_rr_peers_s { ngx_uint_t numbe

nginx負載均衡加權

       當nginx作為代理伺服器時,需要將客戶端的請求轉發給後端伺服器進行處理,如果後端伺服器有多臺,那如何選擇合適的後端伺服器來處理當前請求,也就是本篇文章要介紹的內容。nginx儘可能的把請求分攤到各個後端伺服器進行處理,以保證服務的可用性和可靠行,提供給客戶端

dubbo負載均衡原始碼解讀

首先連線一下概念 整個Dubbo的RPC框架建立在Protocol、Exporter和Invoker這三個介面上,Protocol擴充套件點定義了一種RPC協議的實現,即如何釋出和引用服務,Exporter代表了一個對外暴露的服務,Invoker執行遠端呼叫並返回結

十四.nginxweb反向代理調用加權算法nfs服務

文件夾 是否 觀察 查看 方式 har sys 重新啟動 chm 一.部署nginx反向代理web服務,調度算法使用加權輪詢: 1.首先配置一個nginx服務端,三個web客戶端。用vmware 新建虛擬機完成,並用xshell連接 2.在服務端和3個web客戶端都下載e

基於nginxtengine的tcp反向代理負載均衡 安裝配置

zip http 端口 arc -s pro 進入 iteye obj 先下載nginx_tcp_proxy_module模塊。 wget https://github.com/yaoweibin/nginx_tcp_proxy_module/archive/mast

LVS負載均衡介紹配置

start 外部 響應 數據接收 網通 eal 方便 ive ucc 負載均衡群集介紹? ?? 開源的負載均衡軟件LVS、keepalived、haproxy和nginx等keepalived除了可以實現高可用外,也可以拿來做負載均衡功能LVS是基於4層負載均衡,網絡OSI

dubbo高可用之zookeeper宕機、Dubbo直連、負載均衡、服務降級、叢集容錯

之前我們說了dubbo超時重試啟動檢查等配置,接下來我們說一下dubbo高可用的一些配置 1. zookeeper宕機 我們接下來討論一下如果zookeeper宕機對我們的服務提供者消費者有什麼影響 現象:zookeeper註冊中心宕機,還可以消費dubbo暴露的服務。 原因

負載均衡軟體Web應用伺服器詳解

為什麼我們使用軟體負載均衡 海量併發下分散請求,減輕服務壓力,分散式部署,容災,解決單點問題,埠複用節約伺服器資源 硬體負載均衡成本較高,裝置維護較為複雜,穩定性高 nginx haproxy lvs(piranha) php+fastcgi,modjk+tomcat 軟體負責均衡原理 負載

Nginx的負載均衡max_failsfail_timeout設定

描述 在Nginx的負載均衡檢查模組中,對於負載均衡的節點可以配置如下可選引數引數: max_fails=1 fail_timeout=10s 這個是Nginx在負載均衡功能中,用於判斷後端節點狀態,所用到兩個引數。 Nginx基於連線探測,如果發現後端異常,在單位週期為fail_timeout設定

Nginx的負載均衡max_failsfail_timeout設置

可選參數 oss ado 檢查 col http 說明 blog pre 描述 在Nginx的負載均衡檢查模塊中,對於負載均衡的節點可以配置如下可選參數參數: max_fails=1 fail_timeout=10s 這個是Nginx在負載均衡功能中,用於判斷後端節點狀態

JavaScript事件、微任務巨集任務

JavaScript是單執行緒語言,也就是說同一個事件只能做一件事。雖然HTML5提出了Web Worker,允許JavaScript指令碼建立多個執行緒,但是子執行緒完全受主執行緒控制,且不得操作DOM和BOM。所以,依然沒有改變JavaScript是單執行緒的本質。 為了解決單執行緒導致

硬件負載均衡F5軟負責均衡Nginx

優點 搜索 性能 智能 適用於 訪問 缺點 read ide 請直接搜索相關文章了解:http://www.ideadata.com.cn/wisdomAction/readWisdom.do?id=75 F5,硬件 優點:能夠直接通過智能交換機實現,處理能力更

硬體負載均衡F5軟負責均衡Nginx

  請直接搜尋相關文章瞭解:http://www.ideadata.com.cn/wisdomAction/readWisdom.do?id=75     F5,硬體 優點:能夠直接通過智慧交換機實現,處理能力更強,而且與系統無關,負載效能強,更適用於一大堆裝置、大訪問