Dubbo Cluster集羣那點你不知道的事。

這是why技術的第33篇原創文章git

本週是在家辦公的一週,上面的圖就是我在家的工位。github

工欲善其事,必先利其器。在家辦公,我是認真的。面試

在家裏開發的時候有需求是這樣的:一個若是接口調用失敗,須要自動進行重試。redis

雖然關係不大,可是我仍是想到了Dubbo的集羣容錯策略:Failover Cluster,即失敗自動切換。算法

(這個轉折是否是有點生硬.......)apache

因此借本文對於Dubbo的Cluster集羣和Failover Cluster(失敗自動切換)策略進行一個詳細分析。編程

本文若是沒有特別說明的地方,源碼均是來自最新的2.7.5版本。緩存

在閱讀以前先拋出幾個問題:安全

1.Dubbo Cluster集羣的做用是什麼?服務器

2.Dubbo Cluster的10個實現類你能說出來幾個,其中哪幾個是集羣容錯的方法實現?

3.默認的集羣實現類是什麼呢?

4.Failover Cluster調用失敗以後,會自動進行幾回重試呢?

5.什麼是Dubbo的粘滯鏈接?

6.粘滯鏈接在Cluster中是怎麼應用的?

7.Cluster選擇出一個可用的Invoker最多要進行幾回選擇?

8.請問幾回選擇分別是什麼?

注意:上面的8個問題,前3個是很是常見的面試題。後面的都是你閱讀完本文後就能夠知道問題的答案,面試中並不常見,可是後面的問題能夠綜合成一個很是高頻的面試題:有看過什麼源碼嗎,能給我講講嗎?

本文會對上面的問題進行逐一的、詳細的解讀。文章的最後會進行一個問題和答案的彙總。

廢話很少說,看完以後以爲不錯,還求個關注。抱拳了,老鐵。

Dubbo Cluster集羣的做用是什麼?

在生產環境,咱們經常是多個服務器跑相同的應用,這種作的目的其一是爲了不單點故障。

爲了不單點故障,如今的應用一般至少會部署在兩臺服務器上。而對於一些負載比較高的服務,好比網關服務,會部署更多的服務器。

這樣,在同一環境下的服務提供者數量會大於1。對於服務消費者來講,同一環境下出現了多個服務提供者。

這時會出現幾個問題:對於一次請求,我做爲消費者到底調用哪一個提供者呢?服務調用失敗的時候我怎麼作呢?是重試?是拋出異常?或者僅僅是打印出異常?

爲了處理這些問題,Dubbo定義了集羣接口Cluster以及Cluster Invoker。

集羣Cluster的用途是將多個服務提供者合併爲一個Cluster Invoker,並將這個Invoker暴露給服務消費者。

這樣的好處就是對服務消費者來講,只需經過這個Cluster Invoker進行遠程調用便可,至於具體調用哪一個服務提供者,以及調用失敗後如何處理等問題,如今都交給集羣模塊去處理。

集羣模塊是服務提供者和服務消費者的中間層,爲服務消費者屏蔽了服務提供者的狀況,這樣服務消費者就能夠專心處理遠程調用相關事宜。好比發請求,接受服務提供者返回的數據等。這就是Dubbo Cluster集羣的做用。

Dubbo Cluster的10個實現類是什麼?

根據配置能夠知道Dubbo集羣接口Cluster有10種實現方法以下:

須要注意的是,十種實現方法其中只有failover、failfast、failsafe、failback、forking、broadcast這6種才屬於集羣容錯的範疇。另外的實現均有其餘的應用場景。

下面咱們先說6種集羣容錯的實現方法:

Failover Cluster:

failover=org.apache.dubbo.rpc.cluster.support.FailoverCluster

失敗自動切換,在調用失敗時,失敗自動切換,當出現失敗,重試其它服務器。一般用於讀操做,但重試會帶來更長延遲。可經過retries="2"來設置重試次數(不含第一次)。

Failfast Cluster:

failfast=org.apache.dubbo.rpc.cluster.support.FailfastCluster

快速失敗,只發起一次調用,失敗當即報錯。一般用於非冪等性的寫操做,好比新增記錄。

Failsafe Cluster:

failsafe=org.apache.dubbo.rpc.cluster.support.FailsafeCluster

失敗安全,出現異常時,直接忽略。一般用於寫入審計日誌等操做。

Failback Cluster:

failback=org.apache.dubbo.rpc.cluster.support.FailbackCluster

失敗自動恢復,後臺記錄失敗請求,定時重發。一般用於消息通知操做。

Forking Cluster:

forking=org.apache.dubbo.rpc.cluster.support.ForkingCluster

並行調用多個服務器,只要一個成功即返回。一般用於實時性要求較高的讀操做,但須要浪費更多服務資源。可經過 forks="2" 來設置最大並行數。

Broadcast Cluster:

broadcast=org.apache.dubbo.rpc.cluster.support.BroadcastCluster

廣播調用全部提供者,逐個調用,任意一臺報錯則報錯。一般用於通知全部提供者更新緩存或日誌等本地資源信息。

因此對於這個問題你也能夠回答上來了:10個實現類中有哪幾個是集羣容錯的方法實現?

接下來再說說另外四個實現類:

Available Cluster:

available=org.apache.dubbo.rpc.cluster.support.AvailableCluster

獲取可用的服務方。遍歷全部Invokers經過invoker.isAvalible判斷服務端是否活着,只要一個有爲true,直接調用返回,無論成不成功。

Mergeable Cluster:

mergeable=org.apache.dubbo.rpc.cluster.support.MergeableCluster

分組聚合,將集羣中的調用結果聚合起來,而後再返回結果。好比菜單服務,接口同樣,但有多種實現,用group區分,如今消費方需從每種group中調用一次返回結果,合併結果返回,這樣就能夠實現聚合菜單項。

Mock Cluster:

mock=org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper

本地假裝,一般用於服務降級,好比某驗權服務,當服務提供方所有掛掉後,客戶端不拋出異常,而是經過 Mock 數據返回受權失敗。

zone-aware Cluster:

zone-aware=org.apache.dubbo.rpc.cluster.support.registry.ZoneAwareCluster

上面的幾種Cluster策略在官網都能找到對應的說明,可是對於這個zone-aware目前官網上是沒有介紹的,由於這是前段時間發佈的2.7.5版本才支持的內容,以下圖所示:

因此對於zone-aware這個策略我多說兩句。具體能夠參照下面的這個issue: https://github.com/apache/dubbo/issues/5399

zone-aware的應用場景是下面這樣的。

業務部署假設是雙註冊中心:

則對應消費端,先在註冊中心間選擇,再到選定的註冊中心選址:

因此,和以前相比,在Dubbo 2.7.5之後,對於多註冊中心訂閱的場景,選址時的多了一層註冊中心集羣間的負載均衡。

這個註冊中心集羣間的負載均衡的實現就是:zone-aware Cluster。

對於多註冊中心間的選址策略,根據類上的註釋能夠看出,目前設計的有下面四種:

1.指定優先級:

來自preferred="true"註冊中心的地址將被優先選擇,只有該中心無可用地址時才Fallback到其餘註冊中心

<dubbo:registry address="zookeeper://${zookeeper.address1}" preferred="true" />

2.同 zone 優先:

選址時會和流量中的zone key作匹配,流量會優先派發到相同zone的地址

<dubbo:registry address="zookeeper://${zookeeper.address1}" zone="beijing" />

3.權重輪詢:

來自北京和上海集羣的地址,將以10:1的比例來分配流量

<dubbo:registry id="beijing" address="zookeeper://${zookeeper.address1}" weight="100" />

<dubbo:registry id="shanghai" address="zookeeper://${zookeeper.address2}" weight="10" />

4.默認方法:

選擇第一個可用的便可。

默認的集羣方法是什麼呢?

源碼之下無祕密。咱們從源碼中尋找答案:

首先咱們能夠看到,Cluster是一個SPI接口。其默認實現是FailoverCluster.NAME,以下源碼所示:

因此默認的實現方法就是:

org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker

因爲Cluster是一個SPI接口,因此咱們也能夠根據實際需求去擴展本身的實現類。

FailoverCluster doInvoke源碼解析

接下來咱們就對FailoverClusterInvoker的doInvoke方法的源碼進行解析。

這一小節主要回答這一個問題:Failover Cluster調用失敗以後,會自動切換Invoker進行幾回重試呢?

經過源碼,咱們能夠知道默認的重試次數是2次

有人就問了:爲何第61行的最後還有一個"+1"呢?

你想想。咱們想要在接口調用失敗後,重試n次,這個n就是DEFAULT_RETRIES,默認爲2。那麼咱們總的調用次數就是n+1次了。因此這個"+1"是這樣來的,很小的一個點。

另外圖中標記了紅色五角星★的地方,第62到64行。也是很關鍵的地方。對於retries參數,在官網上的描述是這樣的:

不須要重試請設爲0。咱們前面分析了,當設置爲0的時候,只會調用一次。

可是我也看見過retries配置爲"-1"的。-1+1=0。調用0次明顯是一個錯誤的含義。可是程序也正常運行,且只調用一次。

這就是標記了紅色五角星★的地方的功勞了。防護性編程。哪怕你設置爲-10086也只會調用一次。

接下來對doInvoke方法進行一個全面的解讀,下面是2.7.5版本的源碼,我基本上每一行主要的代碼都加了註釋,能夠點開大圖查看: org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker#doInvoke

如上所示,FailoverClusterInvoker的doInvoke方法主要的工做流程是:

首先是獲取重試次數,而後根據重試次數進行循環調用,在循環體內,若是失敗,則進行重試。

在循環體內,首先是調用父類AbstractClusterInvoker的select方法,經過負載均衡組件選擇一個Invoker,而後再經過這個Invoker的invoke方法進行遠程調用。若是失敗了,記錄下異常,並進行重試。

注意一個細節:在進行重試前,從新獲取最新的invoker集合,這樣作的好處是,若是在重試的過程當中某個服務掛了,能夠經過調用list方法能夠保證copyInvokers是最新的可用的invoker列表。

整個流程大體如此,不是很難理解。

什麼是Dubbo的粘滯鏈接?

接下來咱們要看的是父類AbstractClusterInvoker的select方法的邏輯。可是在看select方法的邏輯以前,我必須得先鋪墊一下Dubbo粘滯鏈接特性的知識。

官網上的解釋是這樣的:

能夠看出,這是一個服務治理類型的參數。當設置true時,該接口上的全部方法使用同一個provider。官方文檔中說明能夠用在接口和方法級別。

這些都是一些比較簡單的服務治理的規則。若是需求更復雜,則須要使用路由功能。

官方文檔已經說的很清楚了。我就只簡單的解釋一下第一句話:粘滯鏈接用於有狀態服務。

那麼什麼是有狀態服務,什麼又是無狀態服務呢?

根據我簡單的理解。對服務提供者來講,到底是有狀態的服務提供者,仍是無狀態服務,其判斷依據就一句話: 從客戶端發起的兩個或者多個請求,在服務端是否具有上下文關係。

舉個例子,咱們常常會用到的session。

衆所周知,HTTP協議是無狀態的。那麼當在一個電商場景下,將用戶挑選的商品放到購物車,保存到session裏,當付款的時候,再從購物車裏取出商品信息。這樣經過session就實現了有狀態的服務。

當一個服務被設計爲無狀態的時候,對於客戶端來講,能夠隨意調用。因此無狀態的服務能夠很容易的進行水平擴容。

當一個服務被設計爲有狀態的時候,想要水平擴容的時候就不是那麼簡單了。由於客戶端和服務端存在着上下文關係,因此客戶端每次都須要請求那一臺服務端。

把一個有狀態的服務修改成無狀態的服務的方案也很簡單。仍是拿session舉例,這個時候,咱們的分佈式session就呼之欲出了。把session集中存儲起來,好比放到redis中,弄一個獨立於服務的session共享層。這樣,一個有狀態的服務就能夠變爲一個無狀態的服務。

AbstractClusterInvoker select源碼解析

看完這一小節,你也就知道了粘滯鏈接在Cluster中是怎麼應用的了。

有了粘滯鏈接的知識儲備後,再看select方法就比較輕鬆了,首先須要知道的是select方法是一個關鍵的公共方法,其做用就是選擇出一個可用的invoke,有下面這幾個Cluster的實現類都在調用:

代碼片斷不長,邏輯也比較清楚,具體代碼解析以下:

根據代碼畫出select方法的流程圖以下:

結合代碼和流程圖,再進行一個文字描述。

先介紹一下select的四個入參,分別是:

loanbalance:負載均衡策略。

invocation:它持有調用過程當中的變量,好比方法名,參數等。

invokers:這裏的invokers列表能夠看作是存活着的服務提供者列表。

selected:已經被選擇過的invoker集合。

經過源碼咱們能夠看出,select方法的主要邏輯集中在了對粘滯鏈接特性的支持上。

首先是獲取sticky配置,而後再檢測invokers列表中是否包含 stickyInvoker,若是不包含,則認爲該stickyInvoker不可用,此時將其置空。

爲何能夠置空?

由於這裏的invokers列表是存活着的服務提供者列表,若是這個列表不包含stickyInvoker,那天然而然的認爲stickyInvoker掛了,因此置空。

接下來,若是stickyInvoker存在於invokers列表中,說明stickyInvoker還活着,此時要進行下一項檢測。檢測selected(選擇過的服務提供者列表)中是否包含 stickyInvoker。

若是包含的話,說明stickyInvoker在此以前沒有成功提供服務(但其仍然處於存活狀態)。此時咱們認爲這個服務不可靠,不該該在重試期間內再次被調用,所以這個時候不會返回該stickyInvoker。

若是selected不包含stickyInvoker,此時還須要進行可用性檢測,好比檢測服務提供者網絡連通性等。當可用性檢測經過,纔可返回 stickyInvoker,不然調用doSelect方法選擇Invoker。

若是sticky爲true,此時會將doSelect方法選出的Invoker賦值給stickyInvoker。

關於粘滯鏈接和可用性檢測的默認配置以下:

即默認狀況下粘滯鏈接是關閉狀態。當粘滯鏈接開啓時,默認會進行可用性檢查。

關於select方法先分析這麼多,繼續向下分析。

AbstractClusterInvoker doSelect源碼解析

讀完這一小節你能夠回答出這兩個問題:

1.Cluster選擇出一個可用的Invoker最多要進行幾回選擇?

2.請問幾回選擇分別是什麼?

doSelect方法的源碼解析以下:

Failover Cluster選擇出一個可用的Invoker最多要進行三次選擇,也是doSelect的主要邏輯,三次分別是(圖中標號了①②③的地方):

①:經過負載均衡組件選擇 Invoker。

②:若是選出來的 Invoker 不穩定,或不可用,此時須要調用reselect 方法進行重選。

③:reselect選出來的Invoker爲空,此時定位invoker在invokers列表中的位置index,而後獲取index+1處的 invoker。

AbstractClusterInvoker reselect源碼解析

下面咱們來看一下 reselect 方法的邏輯。

根據源碼作出流程圖以下:

因此,reselect方法總結下來其實作了四件事情:

第一:查找可用的invoker,並將其添加到reselectInvokers集合中。這個reselectInvokers集合你能夠理解爲裏面放的是全部的可用的invoker的集合與selected集合的差集。

第二:若是通過篩選後,reselectInvokers不爲空,則經過負載均衡組件再次進行選擇並返回。

第三:若是通過篩選後,reselectInvokers爲空,則再從selected集合(已經被調用過的集合)中選出全部可用的invoker,放到reselectInvokers中,再次經過負載均衡組件進行選擇並返回。

第四:若是進過上面的步驟後,沒有選擇出合適的invoker,reselectInvokers仍是爲空,說明全部的invoker都不可用了,返回爲null。

好了,到這裏就把最開始拋出的8個問題都解答完畢了,接下來對問題、答案進行一個彙總。

問題、答案彙總

1.Dubbo Cluster集羣的做用是什麼?

簡單來講:集羣模塊是服務提供者和服務消費者的中間層,爲服務消費者屏蔽了服務提供者的狀況,這樣服務消費者就能夠專心處理遠程調用相關事宜。好比發請求,接受服務提供者返回的數據等。這就是Dubbo Cluster集羣的做用。

2.Dubbo Cluster的10個實現類你能說出來幾個,其中哪幾個是集羣容錯的方法實現?

根據配置能夠知道Dubbo集羣接口Cluster有10種實現方法以下:

其中failover、failfast、failsafe、failback、forking、broadcast這6種才屬於集羣容錯的範疇。另外的實現均有其餘的應用場景。還須要注意的是zone-aware是2.7.5版本後才支持的實現類,以前是registryaware。

3.默認的集羣實現類是什麼呢?

失敗自動切換: org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker

4.Failover Cluster調用失敗以後,會自動切換Invoker進行幾回重試呢?

自動進行2次重試,共計調用3次。

5.什麼是Dubbo的粘滯鏈接?

粘滯鏈接用於有狀態服務,儘量讓客戶端老是向同一提供者發起調用,除非該提供者掛了,再連另外一臺。粘滯鏈接將自動開啓延遲鏈接,以減小長鏈接數。

6.粘滯鏈接在Cluster中是怎麼應用的?

參照AbstractClusterInvoker select源碼解析。select方法的主要邏輯集中在了對粘滯鏈接特性的支持上。

7.Cluster選擇出一個可用的Invoker最多要進行幾回選擇?

最多進行三次選擇。

8.請問幾回選擇分別是什麼?

①:經過負載均衡組件選擇 Invoker。 ②:若是選出來的 Invoker 不穩定,或不可用,此時須要調用reselect 方法進行重選。 ③:reselect選出來的Invoker爲空,此時定位invoker在invokers列表中的位置index,而後獲取index+1處的 invoker。

最後說一句(求個關注)

以前也寫過幾篇Dubbo相關的文章,有興趣的能夠看一看:

《Dubbo 2.7.5在線程模型上的優化》

《快速失敗機制&失敗安全機制》

《夠強!一行代碼就修復了我提的Dubbo的Bug。》

《Dubbo加權輪詢負載均衡的源碼和Bug,瞭解一下?》

《Dubbo一致性哈希負載均衡的源碼和Bug,瞭解一下?》

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

《參加Dubbo社區開發者日成都站後,帶給個人一點思考。》

《Dubbo 2.7新特性之異步化改造》

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

感謝您的閱讀,原創不易,求個關注.

以上。

歡迎關注公衆號【why技術】,堅持輸出原創。願你我共同進步。

相關文章
相關標籤/搜索