Dubbo學習筆記7:Dubbo的集羣容錯與負載均衡策略

Dubbo的集羣容錯策略算法

正常狀況下,當咱們進行系統設計時候,不只要考慮正常邏輯下代碼該如何走,還要考慮異常狀況下代碼邏輯應該怎麼走。當服務消費方調用服務提供方的服務出現錯誤時候,Dubbo提供了多種容錯方案,缺省模式爲failover,也就是失敗重試。緩存

Dubbo提供的集羣容錯模式安全

下面看下Dubbo提供的集羣容錯模式:服務器

Failover Cluster:失敗重試併發

當服務消費方調用服務提供者失敗後自動切換到其餘服務提供者服務器進行重試。這一般用於讀操做或者具備冪等的寫操做,須要注意的是重試會帶來更長延遲。可經過 retries="2" 來設置重試次數(不含第一次)。負載均衡

接口級別配置重試次數方法 <dubbo:reference retries="2" /> ,如上配置當服務消費方調用服務失敗後,會再重試兩次,也就是說最多會作三次調用,這裏的配置對該接口的全部方法生效。固然你也能夠針對某個方法配置重試次數以下:dom

 

<dubbo:reference>
    <dubbo:method name="sayHello" retries="2" />
</dubbo:reference>

 

Failfast Cluster:快速失敗分佈式

當服務消費方調用服務提供者失敗後,當即報錯,也就是隻調用一次。一般這種模式用於非冪等性的寫操做。ide

Failsafe Cluster:失敗安全url

當服務消費者調用服務出現異常時,直接忽略異常。這種模式一般用於寫入審計日誌等操做。

Failback Cluster:失敗自動恢復

當服務消費端用服務出現異常後,在後臺記錄失敗的請求,並按照必定的策略後期再進行重試。這種模式一般用於消息通知操做。

Forking Cluster:並行調用

當消費方調用一個接口方法後,Dubbo Client會並行調用多個服務提供者的服務,只要一個成功即返回。這種模式一般用於實時性要求較高的讀操做,但須要浪費更多服務資源。可經過 forks="2" 來設置最大並行數。

Broadcast Cluster:廣播調用

當消費者調用一個接口方法後,Dubbo Client會逐個調用全部服務提供者,任意一臺調用異常則此次調用就標誌失敗。這種模式一般用於通知全部提供者更新緩存或日誌等本地資源信息。

如上,Dubbo自己提供了豐富的集羣容錯模式,可是若是您有定製化需求,能夠根據Dubbo提供的擴展接口Cluster進行定製。在後面的消費方啓動流程章節會講解什麼時候/如何使用的集羣容錯。

失敗重試策略實現分析

Dubbo中具體實現失敗重試的是FailoverClusterInvoker類,這裏咱們看下具體實現,主要看下doInvoke代碼:

public Result doInvoke(Invocation invocation,final List<Invoker<T>> invokers,LoadBalance loadbalance) throws RpcException{ // (1) 全部服務提供者
    List<Invoker<T>> copyinvokers = invokers; checkInvokers(copyinvokers,invocation); // (2)獲取重試次數
    int len  = getUrl().getMethodParameter(invocation.getMethodName(),Constants.RETRIES_KEY,Constants.DEFAULT_RETRIES) + 1; if(len <= 0){ len = 1; } // (3)使用循環,失敗重試
    RpcException le = null;    // last exception
    List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyinvokers.size()); Set<String> providers = new HashSet<String>(); for(int i=0;i<len;i++){ // 重試時,進行從新選擇,避免重試時invoker列表已發生變化 // 注意:若是列表發生了變化,那麼invoked判斷會失效,由於invoker示例已經改變
        if(i > 0){ // (3.1)
            checkWhetherDestroyed();    // 若是當前實例已經被銷燬,則拋出異常 // (3.2) 從新獲取全部服務提供者
            copyinvokers = list(invocation); // (3.3) 從新檢查一下
 checkInvokers(copyinvokers,invocation); } // (3.4) 選擇負載均衡策略
        Invoker<T> invoker = select(loadbalance,invocation,copyinvokers,invoked); invoked.add(invoker); RpcContext.getContext().setInvokers((List)invoked); // (3.5) 具體發起遠程調用
        try{ Result result = invoker.invoke(invocation); if(le != null && logger.isWarnEnabled()){ ... } return result; }catch(RpcException e){ if(e.isBiz()){    // biz exception
                throw e; } le = e; }catch(Throwable e){ le = new RpcException(e.getMessage(),e); }finally{ providers.add(invoker.getUrl().getAddress()); } } throw new RpcException("拋出異常..."); }
  • 如上代碼(2)從url參數裏面獲取設置的重試次數,若是用戶沒有設置則取默認的值,默認是重試2,這裏須要注意的是代碼(2)是獲取配置重試次數又+1了。這說明 總共調用次數=重試次數+1 (1是正常調用)。
  • 代碼(3)循環重複試用,若是第一次調用成功則直接跳出循環返回,不然循環重試。第一次調用時不會走代碼(3.1)(3.2)(3.3)。若是第一次調用出現異常,則會循環,這時候i=1,因此會執行代碼(3.1)檢查是否有線程調用了當前ReferenceConfig的destroy()方法,銷燬了當前消費者。若是當前消費者實例已經被消費,那麼重試就沒有意義了,因此會拋出RpcException異常。
  • 若是當前消費者實例沒被銷燬,則執行代碼(3.2)從新獲取當前服務提供者列表,這是由於從第一次調開始到線程可能提供者列表已經變化了,獲取列表後,而後執行(3.2)又一次進行了校驗。校驗經過則執行(3.4),根據負載均衡策略選擇一個服務提供者,再次嘗試調用。負載均衡策略的選擇下節會講解。

 Dubbo的負載均衡策略

當服務提供方是集羣的時候,爲了不大量請求一直落到一個或幾個服務提供方機器上,從而使這些機器負載很高,甚至打死,須要作必定的負載均衡策略。Dubbo提供了多種均衡策略,缺省爲random,也就是每次隨機調用一臺服務提供者的機器。

Dubbo提供的負載均衡策略

  • Random LoadBalance:隨機策略。按照機率設置權重,比較均勻,而且能夠動態調節提供者的權重。
  • RoundRobin LoadBalance:輪詢策略。輪詢,按公約後的權重設置輪詢比率。會存在執行比較慢的服務提供者堆積請求的狀況,好比一個機器執行的很是慢,可是機器沒有掛調用(若是掛了,那麼當前機器會從Zookeeper的服務列表刪除),當不少新的請求到達該機器後,因爲以前的請求尚未處理完畢,會致使新的請求被堆積,長此以往,全部消費者調用這臺機器上的請求都被阻塞。
  • LeastActive LoadBalance:最少活躍調用數。若是每一個提供者的活躍數相同,則隨機選擇一個。在每一個服務提供者裏面維護者一個活躍數計數器,用來記錄當前同時處理請求的個數,也就是併發處理任務的個數。因此若是這個值越小說明當前服務提供者處理的速度很快或者當前機器的負載比較低,因此路由選擇時候就選擇該活躍度最小的機器。若是一個服務提供者處理速度很慢,因爲堆積,那麼同時處理的請求就比較多,也就是活躍調用數目越大,這也使得慢的提供者收到更少請求,由於越慢的提供者的活躍度愈來愈大。
  • ConsistentHash LoadBalance:一致性Hash策略。一致性Hash,能夠保證相同參數的請求老是發到同一提供者,當某一臺提供者掛了時,本來發往該提供者的請求,基於虛擬節點,平攤到其餘提供者,不會引發劇烈變更。

一致性Hash負載均衡策略原理

在解決分佈式系統中,負載均衡的問題可使用Hash算法讓固定的一部分請求落到同一臺服務器上,這樣每臺服務器固定處理一部分請求(並維護這些請求的信息),起到負載均衡的做用。

可是普通的餘數Hash(Hash(好比用戶id)%服務器機器數)算法伸縮性不好,當新增或者下線服務器機器時候,用戶id與服務器的映射關係會大量失效。一致性Hash則利用Hash環對其進行了改進。

一致性Hash概述

爲了能直觀的理解一致性Hash的原理,這裏結合一個簡單的例子來說解,假設有4臺服務器,地址爲ip1/ip2/ip3/ip4 。

  •  一致性Hash,首先計算四個ip地址對應的Hash值:hash(ip1) / hash(ip2) / hash(ip3) / hash(ip4) ,計算出來的Hash值是0~最大正整數之間的一個值,這四個值在一致性Hash環上呈現以下圖:

  • Hash環上順時針從整數0開始,一直到最大正整數,咱們根據四個ip計算的Hash值確定會落到這個Hash環上的某一個點,至此咱們把服務器的四個ip映射到了一致性Hash環。
  • 當用戶在客戶端進行請求時候,首先根據Hash(用戶id)計算路由規則(Hash值),而後看Hash值落到了Hash環的哪一個地方,根據Hash值在Hash環上的位置順時針找距離最近的ip做爲路由ip。

如上圖可知user1 / user2的請求會落到服務器 ip2 進行處理,user3的請求會落到服務器ip3進行處理,user4的請求會落到服務器ip4進行處理,user5 / user6 的請求會落到服務器ip1進行處理。

下面考慮當ip2的服務器掛了的時候會出現什麼狀況?

當ip2的服務器掛了的時候,一致性Hash環大體以下圖:

根據順時針規則可知user1 / user2的請求會被服務器ip3進行處理,而其餘用戶的請求對應的處理服務器不變,也就是隻有以前被ip2處理的一部分用戶的映射關係被破壞了,而且其負責處理的請求被順時針下一個節點委託處理。

下面考慮當有新增機器加入時會出現什麼狀況?

當新增一個ip5的服務器後,一致性Hash環大體以下圖:

根據順時針規則可知以前user1的請求應該被ip1服務器處理,如今被新增的ip5服務器處理,其餘用戶的請求處理服務器不變,也就是新增的服務器順時針最近的服務器的一部分請求會被新增的服務器所替代。

一致性Hash的特性

一致性Hash有要有如下特性:

  • 單調性(Monotonicity),單調性是指若是已經有一些請求經過哈希分派到了相應的服務器進行處理,又有新的服務器加入到系統中時,應保證原有的請求能夠被映射到原有的或者新的服務器中去,而不會被映射到原來的其餘服務器上去。這一點經過上面新增服務器ip5能夠證實,新增ip5後,原來被ip1處理的user6如今仍是被ip1處理的user5如今被新增的ip5處理。
  • 分散性(Spread):分佈式環境中,客戶端請求時候可能不知道全部服務器的存在,可能只知道其中一部分服務器,在客戶端看來它看到的部分服務器會造成一個完整的Hash環,那麼可能會致使,同一個用戶的請求被路由到不一樣的服務器進行處理。這種狀況顯然是應該避免的,由於它不能保證同一個用戶的請求落到同一個服務器。所謂分散性是指上述狀況發生的嚴重程度。
  • 平衡性(Balance),平衡性也就是說負載均衡,是指客戶端Hash後的請求應該可以分散到不一樣的服務器上去。一致性Hash能夠作到每一個服務器都進行處理請求,可是不能保證每一個服務器處理的請求的數量大體相同,以下圖:

服務器ip1 / ip2 / ip3通過Hash後落到了一致性Hash環上,從圖中Hash值分佈可知ip1會負責處理大概80%的請求,而ip2和ip3則只會負責處理大概20%的請求,雖然三個機器都在處理請求,可是明顯每一個機器的負載不均衡,這樣稱爲一致性Hash的傾斜,虛擬節點的出現就是爲了解決這個問題。

虛擬節點

當服務器節點比較少的時候會出現上節所說的一致性Hash傾斜的問題,一個解決方法是多加機器,可是加機器是有成本的,那麼就加虛擬節點,好比上面三個機器,每一個機器引入1個虛擬節點後的一致性Hash環以下圖所示:

 

其中ip1-1是ip1的虛擬節點,ip2-1是ip2的虛擬節點,ip3-1是ip3的虛擬節點。

可知當物理機器數目爲M,虛擬節點爲N的時候,實際hash環上節點個數爲 M*(N+1) 。好比當客戶端計算的Hash值處於ip2和ip3或者處於ip2-1和ip3-1之間時候使用ip3服務器進行處理。

均勻一致性Hash

上節咱們使用虛擬節點後的圖看起來比較均衡,可是若是生成虛擬節點的算法不夠好極可能會獲得下面的環:

 

可知每一個服務節點引入1個虛擬節點後,狀況相比沒有引入前均衡性有所改善,可是並不均衡。

均衡的一致性Hash應該以下圖所示:

 

均勻一致性Hash的目標是若是服務器有N臺,客戶端的Hash值有M個,那麼每一個服務器應該處理大概 M/N 個用戶的請求。也就是每臺服務器均衡儘可能均衡。Dubbo提供的一致性Hash就是不均勻的,這個你們能夠去研究下ConsistentHashLoadBalance類。

相關文章
相關標籤/搜索