【dubbo源碼】19. 服務(wù)消費方:集群容錯策略詳解

前言

image

上一篇降到movk本地偽裝其實是對Cluster集群容錯類實例的再次包裝,調(diào)完mockInvoker的invoker方法后,會調(diào)到被包裝的Cluster實例的invoker,對真正的遠程調(diào)用執(zhí)行各種集群容錯的策略。

image

集群容錯策略的選擇

  • 配置方式 :指定集群容錯策略為available`
@Reference(cluster="AvailableCluster")
private UserService userService;
  • 默認策略 :failover

Cluster實例都是從SPI工廠里加載而來的,如果沒有主動配置,默認得到的就是接口上@SPI的值

image
Cluster$Adaptive源代碼 :
public class Cluster$Adaptive implements com.alibaba.dubbo.rpc.cluster.Cluster {
 public com.alibaba.dubbo.rpc.Invoker join(com.alibaba.dubbo.rpc.cluster.Directory arg0) throws com.alibaba.dubbo.rpc.RpcException {
     if (arg0 == null)
         throw new IllegalArgumentException("com.alibaba.dubbo.rpc.cluster.Directory argument == null");
     if (arg0.getUrl() == null)
         throw new IllegalArgumentException("com.alibaba.dubbo.rpc.cluster.Directory argument getUrl() == null");
     // 從url拿cluster的配置,沒有配就failover
     com.alibaba.dubbo.common.URL url = arg0.getUrl();
     String extName = url.getParameter("cluster", "failover");
     if (extName == null)
         throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.cluster.Cluster) name from url(" + url.toString() + ") use keys([cluster])");
     com.alibaba.dubbo.rpc.cluster.Cluster extension = (com.alibaba.dubbo.rpc.cluster.Cluster) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.cluster.Cluster.class).getExtension(extName);
     return extension.join(arg0);
 }
}

FailoverClusterInvoker

當首次調(diào)用失敗后,還會重試指定次數(shù)

循環(huán)最大調(diào)用次數(shù)( 1+重試次數(shù)),每次都去發(fā)起遠程調(diào)用,如果其中一次調(diào)用成功就return,結(jié)束循環(huán),否則繼續(xù)下一次調(diào)用如果超出重試次數(shù)還沒有調(diào)用成功,就拋rpc異常

public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
    List<Invoker<T>> copyinvokers = invokers;
    checkInvokers(copyinvokers, invocation);
    String methodName = RpcUtils.getMethodName(invocation);
    //獲取重試次數(shù)
    int len = getUrl().getMethodParameter(methodName,Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
    if (len <= 0) {
        len = 1;
    }
    // retry loop.
    RpcException le = null; // last exception.
    //已經(jīng)調(diào)用過了的服務(wù)列表
    List<Invoker<T>> invoked = newArrayList<Invoker<T>>(copyinvokers.size()); // invoked invokers.
    Set<String> providers = new HashSet<String>(len);
    for (int i = 0; i < len; i++) {
        //Reselect before retry to avoid a change of candidate `invokers`.
        //NOTE: if `invokers` changed, then `invoked` also lose accuracy.
        //如果掉完一次后变逃,服務(wù)列表更新了奢米,再次獲取服務(wù)列表
        if (i > 0) {
            checkWhetherDestroyed();
            copyinvokers = list(invocation);
            // check again
            checkInvokers(copyinvokers, invocation);
        }
        //根據(jù)負載均衡算法,選擇一個服務(wù)調(diào)用
        Invoker<T> invoker = select(loadbalance, invocation, copyinvokers,invoked);
        //記錄已經(jīng)調(diào)用過的invoker
        invoked.add(invoker);
        RpcContext.getContext().setInvokers((List) invoked);
        try {
            //具體的服務(wù)調(diào)用邏輯
            Result result = invoker.invoke(invocation);
            if (le != null && logger.isWarnEnabled()) {
                logger.warn("Although retry the method " +invocation.getMethodName()
                        + " in the service " + getInterface().getName()
                        + " was successful by the provider " +invoker.getUrl().getAddress()
                        + ", but there have been failed providers " +providers
                        + " (" + providers.size() + "/" +copyinvokers.size()
                        + ") from the registry " +directory.getUrl().getAddress()
                        + " on the consumer " + NetUtils.getLocalHost()
                        + " using the dubbo version " +Version.getVersion() + ". Last error is: "
                        + le.getMessage(), le);
            }
            // 期間有一次調(diào)用成功就返回結(jié)果,結(jié)束循環(huán)
            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());
        }
    }
    // 如果超出重試次數(shù)還沒有調(diào)用成功,就拋rpc異常
    throw new RpcException(le != null ? le.getCode() : 0, "Failed toinvoke the method "
            + invocation.getMethodName() + " in the service " +getInterface().getName()
            + ". Tried " + len + " times of the providers " + providers
            + " (" + providers.size() + "/" + copyinvokers.size()
            + ") from the registry " + directory.getUrl().getAddress()
            + " on the consumer " + NetUtils.getLocalHost() + " using thedubbo version "
            + Version.getVersion() + ". Last error is: "
            + (le != null ? le.getMessage() : ""), le != null && le.getCause() != null ? le.getCause() : le);
}

FailBackClusterInvoker

出錯返回空結(jié)果,異步重試蜗侈,起一個線程朝蜘,等待配置的時間,進行重試

catch到異常

image
image

FailfastClusterInvoker

catch到異常并直接拋出RpcException胞此,不進行重試,以快速響應(yīng)

image

FailsafeClusterInvoker

直接吞掉異常,不進行任何處理,返回空的結(jié)果,適用于對結(jié)果不敏感的業(yè)務(wù)

image

ForkingClusterInvoker

同時調(diào)n臺(默認為2臺)主機臣咖,選擇最快返回結(jié)果的那臺主機返回的結(jié)果

負載均衡后選出與fork數(shù)相同的invoker

image

selecte出幾個invoker就起對應(yīng)數(shù)量的縣城,去發(fā)起遠程調(diào)用,并異步把結(jié)果放到LinkedBlockingQueue 阻塞隊列中

image

一旦從隊列poll到第一個result,就return 結(jié)果

image

BroadcastClusterInvoker

服務(wù)列表里所有主機都會被調(diào),原子廣播
image

AvailableClusterInvoker

沒有負載均衡沒有重試,調(diào)服務(wù)之前會先查看tcp存在有效的長連接,確保后端服務(wù)正常,如果有,再調(diào)用具體的業(yè)務(wù)方法漱牵。用于對數(shù)據(jù)安全要求比較高的業(yè)務(wù)
image
image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末夺蛇,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子酣胀,更是在濱河造成了極大的恐慌刁赦,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闻镶,死亡現(xiàn)場離奇詭異甚脉,居然都是意外死亡,警方通過查閱死者的電腦和手機铆农,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門牺氨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事波闹≡秃溃” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵精堕,是天一觀的道長。 經(jīng)常有香客問我蒲障,道長歹篓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任揉阎,我火速辦了婚禮庄撮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘毙籽。我一直安慰自己洞斯,他們只是感情好,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布坑赡。 她就那樣靜靜地躺著烙如,像睡著了一般。 火紅的嫁衣襯著肌膚如雪毅否。 梳的紋絲不亂的頭發(fā)上亚铁,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天,我揣著相機與錄音螟加,去河邊找鬼徘溢。 笑死,一個胖子當著我的面吹牛捆探,可吹牛的內(nèi)容都是我干的然爆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼黍图,長吁一口氣:“原來是場噩夢啊……” “哼曾雕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起雌隅,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤翻默,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后恰起,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體修械,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年检盼,在試婚紗的時候發(fā)現(xiàn)自己被綠了肯污。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蹦渣,靈堂內(nèi)的尸體忽然破棺而出哄芜,到底是詐尸還是另有隱情,我是刑警寧澤柬唯,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布认臊,位于F島的核電站,受9級特大地震影響锄奢,放射性物質(zhì)發(fā)生泄漏失晴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一拘央、第九天 我趴在偏房一處隱蔽的房頂上張望涂屁。 院中可真熱鬧,春花似錦灰伟、人聲如沸拆又。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽帖族。三九已至,卻和暖如春发笔,著一層夾襖步出監(jiān)牢的瞬間盟萨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工了讨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留捻激,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓前计,卻偏偏與公主長得像胞谭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子男杈,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容