专栏首页大数据进阶dubbo源码分析之-Please check registry access list (whitelist/blacklist)

dubbo源码分析之-Please check registry access list (whitelist/blacklist)

业务方在使用时经常会出现如下错误

看起来像是黑白名单,但是实际是服务方通过zk通知过来的地址变成了EMPTY://这样的形式,通常我们会提供如下的解决方式

一般都能解决问题

今天我们通过源代码来进行一下简单的分析,先来看一下代码的UML图

异常出错的地方是在RegistryDirectory,此类实现了NotifyListener接口,该接口中有一个很重要的方法就是notify,是处理zk的watch事件的,当consumer端监听的节点发生数据变化时,会通知consumer端进行响应的处理,下面我们大概浏览一下代码

//处理zk的watch事件
public synchronized void notify(List<URL> urls) {
    List<URL> invokerUrls = new ArrayList<URL>();
    List<URL> routerUrls = new ArrayList<URL>();
    List<URL> configuratorUrls = new ArrayList<URL>();
    for (URL url : urls) {
        String protocol = url.getProtocol();
        String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
        if (Constants.ROUTERS_CATEGORY.equals(category) 
                || Constants.ROUTE_PROTOCOL.equals(protocol)) {
            routerUrls.add(url);
        } else if (Constants.CONFIGURATORS_CATEGORY.equals(category) 
                || Constants.OVERRIDE_PROTOCOL.equals(protocol)) {
            configuratorUrls.add(url);
        } else if (Constants.PROVIDERS_CATEGORY.equals(category)) {
            invokerUrls.add(url);
        } else {
            logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost());
        }
    }
    // configurators 
    if (configuratorUrls != null && configuratorUrls.size() >0 ){
        this.configurators = toConfigurators(configuratorUrls);
    }
    // routers
    if (routerUrls != null && routerUrls.size() >0 ){
        List<Router> routers = toRouters(routerUrls);
        if(routers != null){ // null - do nothing
            setRouters(routers);
        }
    }
    List<Configurator> localConfigurators = this.configurators; // local reference
    // 合并override参数
    this.overrideDirectoryUrl = directoryUrl;
    if (localConfigurators != null && localConfigurators.size() > 0) {
        for (Configurator configurator : localConfigurators) {
            this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
        }
    }
    // providers
    refreshInvoker(invokerUrls);
}
/**
 * 根据invokerURL列表转换为invoker列表。转换规则如下:
 * 1.如果url已经被转换为invoker,则不在重新引用,直接从缓存中获取,注意如果url中任何一个参数变更也会重新引用
 * 2.如果传入的invoker列表不为空,则表示最新的invoker列表
 * 3.如果传入的invokerUrl列表是空,则表示只是下发的override规则或route规则,需要重新交叉对比,决定是否需要重新引用。
 * @param invokerUrls 传入的参数不能为null
 */
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列表
        Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 换方法名映射Invoker列表
        // 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);
        }
    }
}

下面来看一下EMPTY URL是哪里来的

代码ZookeeperRegistry.java

private List<URL> toUrlsWithEmpty(URL consumer, String path, List<String> providers) {
    List<URL> urls = toUrlsWithoutEmpty(consumer, providers);
    if (urls == null || urls.isEmpty()) {
       int i = path.lastIndexOf('/');
       String category = i < 0 ? path : path.substring(i + 1);
       //这里会添加empty url
       URL empty = consumer.setProtocol(Constants.EMPTY_PROTOCOL).addParameter(Constants.CATEGORY_KEY, category);
        urls.add(empty);
    }
    return urls;
}
private List<URL> toUrlsWithoutEmpty(URL consumer, List<String> providers) {
   List<URL> urls = new ArrayList<URL>();
    if (providers != null && providers.size() > 0) {
        for (String provider : providers) {
            provider = URL.decode(provider);
            if (provider.contains("://")) {
                URL url = URL.valueOf(provider);
                if (UrlUtils.isMatch(consumer, url)) {
                    urls.add(url);
                }
            }
        }
    }
    return urls;
}
//关键是这里会进行URL的过滤
public static boolean isMatch(URL consumerUrl, URL providerUrl) {
    //比较接口
    String consumerInterface = consumerUrl.getServiceInterface();
    String providerInterface = providerUrl.getServiceInterface();
    if( ! (Constants.ANY_VALUE.equals(consumerInterface) || StringUtils.isEquals(consumerInterface, providerInterface)) ) return false;
    
    //比较目录(这里是在哪里用到的,占时还没有搞懂)
    if (! isMatchCategory(providerUrl.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY), 
            consumerUrl.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY))) {
        return false;
    }
    if (! providerUrl.getParameter(Constants.ENABLED_KEY, true) 
            && ! Constants.ANY_VALUE.equals(consumerUrl.getParameter(Constants.ENABLED_KEY))) {
        return false;
    }
   
    String consumerGroup = consumerUrl.getParameter(Constants.GROUP_KEY);
    String consumerVersion = consumerUrl.getParameter(Constants.VERSION_KEY);
    String consumerClassifier = consumerUrl.getParameter(Constants.CLASSIFIER_KEY, Constants.ANY_VALUE);
    
    String providerGroup = providerUrl.getParameter(Constants.GROUP_KEY);
    String providerVersion = providerUrl.getParameter(Constants.VERSION_KEY);
    String providerClassifier = providerUrl.getParameter(Constants.CLASSIFIER_KEY, Constants.ANY_VALUE);
//group,version,classifier    
return (Constants.ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup))
           && (Constants.ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion))
           && (consumerClassifier == null || Constants.ANY_VALUE.equals(consumerClassifier) || StringUtils.isEquals(consumerClassifier, providerClassifier));
}

这里过滤完后,会添加一个empty url,然后就会将forbidden关键字至为true,后面客户方调用时,就会直接抛出异常,提示不可用

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • dubbo源码分析之--java.lang.IllegalStateException: urls to invokers error .invokerUrls.size :1, invoker.si

    最近在生产环境和预发环境经常出现java.lang.IllegalStateException: urls to invokers error .invoker...

    yiduwangkai
  • flink系列(7)-streamGraph

    StreamGraph是flink四层执行图中的第一层图,代码在org.apache.flink.streaming.api.graph包中,第一层graph主...

    yiduwangkai
  • dubbo源代码-config

    yiduwangkai
  • Vim多窗口编辑

       在Linux中使用vim编辑多个窗口     方式:            1、 vim -o file1 file2             打...

    ccf19881030
  • 端口管理

    幽鸿
  • 【基础进阶】URL详解与URL编码

    Sb_Coco
  • Spring Cloud 微服务(九)- 集成 Spring Boot Admin

    SBA(spring-boot-admin) 可简单理解为一个 UI 组件,提供 Endpoint 接口数据的界面展示。

    安宁
  • 简述TCP的三次握手过程

    对方主动关闭连接或者网络异常导致连接中断,这时我方的状态会变成CLOSE_WAIT 此时我方要调用close()来使得连接正确关闭

    ITer.996
  • 探索ASP.NET MVC5系列之~~~4.模型篇---包含模型常用特性和过度提交防御

    其实任何资料里面的任何知识点都无所谓,都是不重要的,重要的是学习方法,自行摸索的过程(不妥之处欢迎指正) 汇总:http://www.cnblogs.com/d...

    逸鹏
  • 额度授予模型

    生活中基本上大多数人都和金融打过交道,尤其现在的时代,金融与我们更接近,金融无处不在,未来就是不在网点。生活的场景嵌入金融方便了支付、信贷、理财等。对于信贷而言...

    机器思维研究院

扫码关注云+社区

领取腾讯云代金券