(五)禁用启用提供者

##禁用启用提供者

禁用配置

管理后台:dubbo-admin,或叫治理中心
在dubbo的治理中心,动态配置上配置
disabled=true
此外,enabled=true覆盖规则是否生效,可不填,缺省生效。


接口的动态配置 注册到zookeeper 的节点/dubbo/接口全包名/configurators
override://192.168.204.79:20880/lam.dubbo.api.LoginService?category=configurators&disabled=true&dynamic=false&enabled=true, override://192.168.56.1/lam.dubbo.api.LoginService?category=configurators&dynamic=false&enabled=true&mock=fail:return+null


并通知 提供者的订阅者,比如消费者
05-05 11:23:10:INFO(422) com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry -  [DUBBO] Notify urls for subscribe url provider://192.168.56.1:20880/lam.dubbo.api.LoginService?anyhost=true&application=lam-dubbo-provider&category=configurators&charset=UTF-8&check=false&default.accepts=0&default.actives=0&default.buffer=8192&default.charset=UTF-8&default.client=netty&default.cluster=failover&default.connections=0&default.executes=0&default.loadbalance=random&default.owner=sky&default.payload=88388608&default.proxy=javassist&default.retries=2&default.serialization=hessian2&default.server=netty&default.threadpool=fixed&default.threads=100&default.timeout=1000&default.token=false&default.weight=100&dispatcher=all&dubbo=2.5.3&interface=lam.dubbo.api.LoginService&loadbalance=roundrobin&methods=logout,login&owner=skylin&pid=10724&serialization=hessian2&side=provider&threadpool=fixed&threads=100&timestamp=1493885611668&transporter=netty, urls: [override://192.168.204.79:20880/lam.dubbo.api.LoginService?category=configurators&disabled=true&dynamic=false&enabled=true, override://192.168.56.1/lam.dubbo.api.LoginService?category=configurators&dynamic=false&enabled=true&mock=fail:return+null], dubbo version: 2.5.3, current host: 127.0.0.1
 

源码原理

提供者的订阅者,会刷新本地的提供者列表,代码如下:

private void refreshInvoker(List<URL> invokerUrls){
    if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null
            && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
        this.forbidden = true; // 禁止访问
        this.methodInvokerMap = null; // 置空列表
        destroyAllInvokers(); // 关闭所有Invoker
    } else {
        this.forbidden = false; // 允许访问
        Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
        if (invokerUrls.size() == 0 && this.cachedInvokerUrls != null){
            invokerUrls.addAll(this.cachedInvokerUrls);
        } else {
            this.cachedInvokerUrls = new HashSet<URL>();
            this.cachedInvokerUrls.addAll(invokerUrls);//缓存invokerUrls列表,便于交叉对比
        }
        if (invokerUrls.size() ==0 ){
            return;
        }
        Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls) ;// 将URL列表转成Invoker列表 //1处
        Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 换方法名映射Invoker列表  //11处
        // state change
        //如果计算错误,则不进行处理.
        if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0 ){
            logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :"+invokerUrls.size() + ", invoker.size :0. urls :"+invokerUrls.toString()));
            return ;
        }
        this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
        this.urlInvokerMap = newUrlInvokerMap;
        try{
            destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap); // 关闭未使用的Invoker
        }catch (Exception e) {
            logger.warn("destroyUnusedInvokers error. ", e);
        }
    }
}

在代码1处,toInvokers方法将url转成调用者Invoker的列表,这里会有服务禁用、启用功能的实现,看下面方法的实现代码:
在代码11处,toMethodInvokers方法将调用者Invoker的列表,转成方法名映射Invoker的列表,这里会有路由功能的实现,介绍完禁用服务功能,下面会介绍路由功能。

/**
 * 将urls转成invokers,如果url已经被refer过,不再重新引用。
 */
private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
    Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<String, Invoker<T>>();
    if(urls == null || urls.size() == 0){
        return newUrlInvokerMap;
    }
    Set<String> keys = new HashSet<String>();
    String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY);
    for (URL providerUrl : urls) {
        //如果reference端配置了protocol,则只选择匹配的protocol
        if (queryProtocols != null && queryProtocols.length() >0) {
            boolean accept = false;
            String[] acceptProtocols = queryProtocols.split(",");
            for (String acceptProtocol : acceptProtocols) {
                if (providerUrl.getProtocol().equals(acceptProtocol)) {
                    accept = true;
                    break;
                }
            }
            if (!accept) {
                continue;
            }
        }
        if (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
            continue;
        }
        if (! ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
            logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() + " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost() 
                    + ", supported protocol: "+ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
            continue;
        }
        URL url = mergeUrl(providerUrl);
        
        String key = url.toFullString(); // URL参数是排序的
        if (keys.contains(key)) { // 重复URL
            continue;
        }
        keys.add(key);
        // 缓存key为没有合并消费端参数的URL,不管消费端如何合并参数,如果服务端URL发生变化,则重新refer
        Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
        Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
        if (invoker == null) { // 缓存中没有,重新refer
            try {
                boolean enabled = true;
                if (url.hasParameter(Constants.DISABLED_KEY)) { //2处
                    enabled = ! url.getParameter(Constants.DISABLED_KEY, false);
                } else {
                    enabled = url.getParameter(Constants.ENABLED_KEY, true);
                }
                if (enabled) {
                    invoker = new InvokerDelegete<T>(protocol.refer(serviceType, url), url, providerUrl);
                }
            } catch (Throwable t) {
                logger.error("Failed to refer invoker for interface:"+serviceType+",url:("+url+")" + t.getMessage(), t);
            }
            if (invoker != null) { // 将新的引用放入缓存
                newUrlInvokerMap.put(key, invoker);
            }
        }else {
            newUrlInvokerMap.put(key, invoker);
        }
    }
    keys.clear();
    return newUrlInvokerMap;
}

在代码2处开始,如果url中包含key disabled,且key的值为true,则该提供者将不会转成调用者Invoker,消费者本地的提供者列表则不包含该提供者,那么消费者在调用提供者时,就不会再调用到该提供者。

处理完提供者列表后,接着将提供者列表转成方法名映射提供者关系,即消费者调用了某个方法,会根据方法映射到提供者,代码如下:

/**
 * 将invokers列表转成与方法的映射关系
 * 
 * @param invokersMap Invoker列表
 * @return Invoker与方法的映射关系
 */
private Map<String, List<Invoker<T>>> toMethodInvokers(Map<String, Invoker<T>> invokersMap) {
    Map<String, List<Invoker<T>>> newMethodInvokerMap = new HashMap<String, List<Invoker<T>>>();
    // 按提供者URL所声明的methods分类,兼容注册中心执行路由过滤掉的methods
    List<Invoker<T>> invokersList = new ArrayList<Invoker<T>>();
    if (invokersMap != null && invokersMap.size() > 0) {
        for (Invoker<T> invoker : invokersMap.values()) {
            String parameter = invoker.getUrl().getParameter(Constants.METHODS_KEY);
            if (parameter != null && parameter.length() > 0) {
                String[] methods = Constants.COMMA_SPLIT_PATTERN.split(parameter);
                if (methods != null && methods.length > 0) {
                    for (String method : methods) {
                        if (method != null && method.length() > 0 && ! Constants.ANY_VALUE.equals(method)) {
                            List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
                            if (methodInvokers == null) {
                                methodInvokers = new ArrayList<Invoker<T>>();
                                newMethodInvokerMap.put(method, methodInvokers);
                            }
                            methodInvokers.add(invoker);
                        }
                    }
                }
            }
            invokersList.add(invoker);
        }
    }
    newMethodInvokerMap.put(Constants.ANY_VALUE, invokersList);
    if (serviceMethods != null && serviceMethods.length > 0) {
        for (String method : serviceMethods) {
            List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
            if (methodInvokers == null || methodInvokers.size() == 0) {
                methodInvokers = invokersList;
            }
            newMethodInvokerMap.put(method, route(methodInvokers, method)); //3处
        }
    }
    // sort and unmodifiable
    for (String method : new HashSet<String>(newMethodInvokerMap.keySet())) {
        List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
        Collections.sort(methodInvokers, InvokerComparator.getComparator());
        newMethodInvokerMap.put(method, Collections.unmodifiableList(methodInvokers));
    }
    return Collections.unmodifiableMap(newMethodInvokerMap);
}

在上面代码的3处,route方法实现了路由功能,路由功能在下章节讲到点击打开链接

自己写了个RPC:

https://github.com/nytta

可以给个star,^0^.

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值