【dubbo源码】19. 服务消费方:集群容错策略详解

前言

image

上一篇降到movk本地伪装其实是对Cluster集群容错类实例的再次包装,调完mockInvoker的invoker方法后,会调到被包装的Cluster实例的invoker,对真正的远程调用执行各种集群容错的策略。

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

当首次调用失败后,还会重试指定次数

循环最大调用次数( 1+重试次数),每次都去发起远程调用,如果其中一次调用成功就return,结束循环,否则继续下一次调用如果超出重试次数还没有调用成功,就抛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);
    //获取重试次数
    int len = getUrl().getMethodParameter(methodName,Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
    if (len <= 0) {
        len = 1;
    }
    // retry loop.
    RpcException le = null; // last exception.
    //已经调用过了的服务列表
    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.
        //如果掉完一次后,服务列表更新了,再次获取服务列表
        if (i > 0) {
            checkWhetherDestroyed();
            copyinvokers = list(invocation);
            // check again
            checkInvokers(copyinvokers, invocation);
        }
        //根据负载均衡算法,选择一个服务调用
        Invoker<T> invoker = select(loadbalance, invocation, copyinvokers,invoked);
        //记录已经调用过的invoker
        invoked.add(invoker);
        RpcContext.getContext().setInvokers((List) invoked);
        try {
            //具体的服务调用逻辑
            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);
            }
            // 期间有一次调用成功就返回结果,结束循环
            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());
        }
    }
    // 如果超出重试次数还没有调用成功,就抛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

出错返回空结果,异步重试,起一个线程,等待配置的时间,进行重试

catch到异常

image
image

FailfastClusterInvoker

catch到异常并直接抛出RpcException,不进行重试,以快速响应

image

FailsafeClusterInvoker

直接吞掉异常,不进行任何处理,返回空的结果,适用于对结果不敏感的业务

image

ForkingClusterInvoker

同时调n台(默认为2台)主机,选择最快返回结果的那台主机返回的结果

负载均衡后选出与fork数相同的invoker

image

selecte出几个invoker就起对应数量的县城,去发起远程调用,并异步把结果放到LinkedBlockingQueue 阻塞队列中

image

一旦从队列poll到第一个result,就return 结果

image

BroadcastClusterInvoker

服务列表里所有主机都会被调,原子广播
image

AvailableClusterInvoker

没有负载均衡没有重试,调服务之前会先查看tcp存在有效的长连接,确保后端服务正常,如果有,再调用具体的业务方法。用于对数据安全要求比较高的业务
image
image
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容