美文网首页dubbo
Dubbo consumer-provider 服务匹配分析

Dubbo consumer-provider 服务匹配分析

作者: 晴天哥_王志 | 来源:发表于2019-10-24 21:47 被阅读0次

    开篇

     这篇文章用于分析Dubbo consumer匹配provider端URL的逻辑,一个简单的场景如在provider侧提供多版本的service的时候,consumer侧能够根据版本匹配到正确的接口并进行访问。

     通过这篇后如果发现provider侧服务发布,但是consumer端没有发现服务,就应该考虑到provider和consumer侧的服务的不匹配。

    consumer服务匹配逻辑分析

    • 获取path目录下所有服务提供者children ,List<String> children = zkClient.addChildListener(path, zkListener)。
    • 针对所有的URL的集合children通过toUrlsWithEmpty(url, path, children)进行匹配逻辑判断。
    public class ZookeeperRegistry extends FailbackRegistry {
    
        public void doSubscribe(final URL url, final NotifyListener listener) {
            try {
                if (ANY_VALUE.equals(url.getServiceInterface())) {
                       // 忽略这部分代码
                } else {
                    List<URL> urls = new ArrayList<>();
                    for (String path : toCategoriesPath(url)) {
                        ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
                        if (listeners == null) {
                            zkListeners.putIfAbsent(url, new ConcurrentHashMap<>());
                            listeners = zkListeners.get(url);
                        }
                        ChildListener zkListener = listeners.get(listener);
                        if (zkListener == null) {
                            listeners.putIfAbsent(listener, 
                            (parentPath, currentChilds) -> ZookeeperRegistry.this.notify(
                                   url, listener, toUrlsWithEmpty(url, parentPath, currentChilds)));
                            zkListener = listeners.get(listener);
                        }
                        zkClient.create(path, false);
                        // 获取providers、routes等目录的子节点
                        List<String> children = zkClient.addChildListener(path, zkListener);
                        if (children != null) {
                            // 添加符合条件的URL连接,通过toUrlsWithEmpty()内部进行判断
                            urls.addAll(toUrlsWithEmpty(url, path, children));
                        }
                    }
                    notify(url, listener, urls);
                }
            } catch (Throwable e) {
                
            }
        }
    }
    
    • toUrlsWithEmpty()内部调用toUrlsWithoutEmpty()方法。
    public class ZookeeperRegistry extends FailbackRegistry {
    
        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(PATH_SEPARATOR);
                String category = i < 0 ? path : path.substring(i + 1);
                URL empty = URLBuilder.from(consumer)
                        .setProtocol(EMPTY_PROTOCOL)
                        .addParameter(CATEGORY_KEY, category)
                        .build();
                urls.add(empty);
            }
            return urls;
        }
    }
    
    • 针对每个provider执行isMatch()的逻辑匹配。
    • 逻辑匹配逻辑在UrlUtils.isMatch()当中匹配。
    public class ZookeeperRegistry extends FailbackRegistry {
    
        private List<URL> toUrlsWithoutEmpty(URL consumer, List<String> providers) {
            List<URL> urls = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(providers)) {
                for (String provider : providers) {
                    // 针对每个provider执行isMatch()的逻辑匹配
                    provider = URL.decode(provider);
                    if (provider.contains(PROTOCOL_SEPARATOR)) {
                        URL url = URL.valueOf(provider);
                        if (UrlUtils.isMatch(consumer, url)) {
                            urls.add(url);
                        }
                    }
                }
            }
            return urls;
        }
    }
    
    • 核心的匹配逻辑在isMatch()方法当中。
    • 前置判断Interface是否相同。
    • 前置判断category是否相同。
    • 前置判断enabled是否为true。
    • 判断provider和consumer的Group+Version+Classifier三者是否相同,不相同就返回false。
    public class UrlUtils {
    
        public static boolean isMatch(URL consumerUrl, URL providerUrl) {
            String consumerInterface = consumerUrl.getServiceInterface();
            String providerInterface = providerUrl.getServiceInterface();
            // 比较Interface的服务名是否相同,同时考虑了"*"这种逻辑。
            if (!(ANY_VALUE.equals(consumerInterface)
                    || ANY_VALUE.equals(providerInterface)
                    || StringUtils.isEquals(consumerInterface, providerInterface))) {
                return false;
            }
            // 比较category是否相同
            if (!isMatchCategory(providerUrl.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY),
                    consumerUrl.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY))) {
                return false;
            }
            // 比较enabled是否相同
            if (!providerUrl.getParameter(ENABLED_KEY, true)
                    && !ANY_VALUE.equals(consumerUrl.getParameter(ENABLED_KEY))) {
                return false;
            }
            // 比较consumerGroup、consumerVersion 、consumerClassifier 三者关系
            String consumerGroup = consumerUrl.getParameter(GROUP_KEY);
            String consumerVersion = consumerUrl.getParameter(VERSION_KEY);
            String consumerClassifier = consumerUrl.getParameter(CLASSIFIER_KEY, ANY_VALUE);
    
            String providerGroup = providerUrl.getParameter(GROUP_KEY);
            String providerVersion = providerUrl.getParameter(VERSION_KEY);
            String providerClassifier = providerUrl.getParameter(CLASSIFIER_KEY, ANY_VALUE);
            // 只有Group+Version+Classifier三者相同的才相等。
            return (ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) 
                         || StringUtils.isContains(consumerGroup, providerGroup))
                    && (ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion))
                    && (consumerClassifier == null || ANY_VALUE.equals(consumerClassifier) 
                           || StringUtils.isEquals(consumerClassifier, providerClassifier));
        }
    }
    

    总结

    • consumer和provider在服务的核心匹配逻辑在于判断Group+Version+Classifier三者是否相同。

    相关文章

      网友评论

        本文标题:Dubbo consumer-provider 服务匹配分析

        本文链接:https://www.haomeiwen.com/subject/nnbdvctx.html