Dubbo源码学习10

RegistryProtocol.export服务导出流程:导出服务ExporterChangeableWrapper->注册服务到注册中心->订阅注册中心overrideSubscribeUrl数据;篇幅有限,本篇幅主要分析订阅override数据

RegistryProtocol.export(final Invoker<T> invoker)

@Override
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        //export invoker
        //导出服务
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
        // 获取注册中心 URL,以 zookeeper 注册中心为例,得到的示例 URL 如下:
        // zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&export=dubbo%3A%2F%2F172.17.48.52%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider
        URL registryUrl = getRegistryUrl(originInvoker);

        //registry provider
        // 根据 URL 加载 Registry 实现类,比如 ZookeeperRegistry
        final Registry registry = getRegistry(originInvoker);
        // 获取已注册的服务提供者 URL,比如:
        // dubbo://169.254.22.149:20880/com.alibaba.dubbo.study.day01.xml.service.EchoService?addListener.1.callback=true&addListener.retries=2&anyhost=true&application=echo-provider&bean.name=com.alibaba.dubbo.study.day01.xml.service.EchoService&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=6688&side=provider&timestamp=1572936570702
        final URL registeredProviderUrl = getRegisteredProviderUrl(originInvoker);

        //to judge to delay publish whether or not
        //获取register参数;register表示是否注册到注册中心
        boolean register = registeredProviderUrl.getParameter("register", true);
        //缓存到ProviderConsumerRegTable的表中
        ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registeredProviderUrl);
        //注册服务到zookeeper
        if (register) {
            register(registryUrl, registeredProviderUrl);
            //找到该originInvoker对应的ProviderInvokerWrapper设置reg属性为true
            ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true);
        }

        // Subscribe the override data
        // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call the same service. Because the subscribed is cached key with the name of the service, it causes the subscription information to cover.
        // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registeredProviderUrl);
        //创建监听器
        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
        //放入overrideSubscribeUrl对应的OverrideListener
        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
        // 向注册中心进行订阅 override 数据
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
        //Ensure that a new exporter instance is returned every time export
        //创建并返回DestroyableExporter
        return new DestroyableExporter<T>(exporter, originInvoker, overrideSubscribeUrl, registeredProviderUrl);
    }

首先根据regiseredProviderUrl构造出overrideSubscribeUrl,创建overrideSubscribeListener对象,放入到缓存overrideListeners(key为overrideSubscribeUrl,value为OverrideListener),注册中心订阅override数据,最后new 一个DestroyableExporter类型的对象返回。

OverrideListener.java

  • NotifyListener.java:收到服务变更时(即当/dubbo/service/configurators节点下内容发生变更),触发该类的notify方法
public interface NotifyListener {

    /**
     * 当收到服务变更通知时触发。
     * <p>
     * 通知需处理契约:<br>
     * 1. 总是以服务接口和数据类型为维度全量通知,即不会通知一个服务的同类型的部分数据,用户不需要对比上一次通知结果。<br>
     * 2. 订阅时的第一次通知,必须是一个服务的所有类型数据的全量通知。<br>
     * 3. 中途变更时,允许不同类型的数据分开通知,比如:providers, consumers, routers, overrides,允许只通知其中一种类型,但该类型的数据必须是全量的,不是增量的。<br>
     * 4. 如果一种类型的数据为空,需通知一个empty协议并带category参数的标识性URL数据。<br>
     * 5. 通知者(即注册中心实现)需保证通知的顺序,比如:单线程推送,队列串行化,带版本对比。<br>
     *
     * @param urls 已注册信息列表,总不为空,含义同{@link com.alibaba.dubbo.registry.RegistryService#lookup(URL)}的返回值。
     */
    void notify(List<URL> urls);
}
  • OverrideListener.java:该类实现了NotifyListenern接口,用户重新暴露服务
/**
         * provider://169.254.22.149:20880/com.alibaba.dubbo.study.day01.xml.service.EchoService?addListener.1.callback=true
         * &addListener.retries=2&anyhost=true&application=echo-provider&bean.name=com.alibaba.dubbo.study.day01.xml.service.EchoService
         * &category=configurators&check=false&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService
         * &methods=echo,addListener&pid=19992&side=provider&timestamp=1573183625289
         */
        private final URL subscribeUrl;
        private final Invoker originInvoker;

getSubscribedOverrideUrl(registeredProviderUrl)

/**
     * 1.将协议修改为provider
     * 2.添加category="configurators"和check=false
     */
    private URL getSubscribedOverrideUrl(URL registedProviderUrl) {
        return registedProviderUrl.setProtocol(Constants.PROVIDER_PROTOCOL)
                .addParameters(Constants.CATEGORY_KEY, Constants.CONFIGURATORS_CATEGORY,
                        Constants.CHECK_KEY, String.valueOf(false));
    }

返回的URL格式:provider://169.254.22.149:20880/com.alibaba.dubbo.study.day01.xml.service.EchoService?addListener.1.callback=true&addListener.retries=2&anyhost=true&application=echo-provider&bean.name=com.alibaba.dubbo.study.day01.xml.service.EchoService&category=configurators&check=false&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=19992&side=provider&timestamp=1573183625289

FailbackRegistry.subscribe(URL url,NotifyListener listener)

@Override
    public void subscribe(URL url, NotifyListener listener) {
        //将当前url对应的Listener放入缓存集合
        super.subscribe(url, listener);
        //从failedSubscribed/failedUnsubscribed集合中删除该OverrideListener
        removeFailedSubscribed(url, listener);
        try {
            // Sending a subscription request to the server side
            //发送订阅请求到服务器端
            doSubscribe(url, listener);
        } catch (Exception e) {
            Throwable t = e;
            //获取该url对应的缓存url TODO 不知道做什么的
            List<URL> urls = getCacheUrls(url);
            if (urls != null && !urls.isEmpty()) {
                notify(url, listener, urls);
                logger.error("Failed to subscribe " + url + ", Using cached list: " + urls + " from cache file: " + getUrl().getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/dubbo-registry-" + url.getHost() + ".cache") + ", cause: " + t.getMessage(), t);
            } else {
                // If the startup detection is opened, the Exception is thrown directly.
                //如果开启了check = true,直接抛出异常
                boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
                        && url.getParameter(Constants.CHECK_KEY, true);
                boolean skipFailback = t instanceof SkipFailbackWrapperException;
                if (check || skipFailback) {
                    if (skipFailback) {
                        t = t.getCause();
                    }
                    throw new IllegalStateException("Failed to subscribe " + url + ", cause: " + t.getMessage(), t);
                } else {
                    logger.error("Failed to subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
                }
            }

            // Record a failed registration request to a failed list, retry regularly
            addFailedSubscribed(url, listener);
        }
    }

调用父类AbstractRegistry的subscribe方法,将url,listener放入缓存中代码如下;不难看出一个overrdeSubscribeUrl有多个NotifyListener

@Override
    public void subscribe(URL url, NotifyListener listener) {
        if (url == null) {
            throw new IllegalArgumentException("subscribe url == null");
        }
        if (listener == null) {
            throw new IllegalArgumentException("subscribe listener == null");
        }
        if (logger.isInfoEnabled()) {
            logger.info("Subscribe: " + url);
        }
        //key为overrideSuscribeUrl类似如下格式
        //listener
        // provider://169.254.22.149:20880/com.alibaba.dubbo.study.day01.xml.service.EchoService?anyhost=true&application=echo-provider&bean.name=com.alibaba.dubbo.study.day01.xml.service.EchoService&category=configurators&check=false&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo&pid=3720&side=provider&timestamp=1571881716824
        Set<NotifyListener> listeners = subscribed.get(url);
        if (listeners == null) {
            subscribed.putIfAbsent(url, new ConcurrentHashSet<NotifyListener>());
            listeners = subscribed.get(url);
        }
        listeners.add(listener);
    }

从failedSubscribed/failedUnsubscribed中overrideSubscribeUrl所对应的监听器集合中删除overrideSubscribeListener实例,从failedNotified中获取overrideSubscrbieUrl通知失败的map Map<NotifyListener, List<URL>>,从中删除掉该NotifyListener实例以及其需要通知的所有的url

 private void removeFailedSubscribed(URL url, NotifyListener listener) {
        //从failedSubscribed/failedUnsubscribed中overrideSubscribeUrl所对应的监听器集合中删除overrideSubscribeListener实例
        Set<NotifyListener> listeners = failedSubscribed.get(url);
        if (listeners != null) {
            listeners.remove(listener);
        }
        //
        listeners = failedUnsubscribed.get(url);
        if (listeners != null) {
            listeners.remove(listener);
        }
        //从failedNotified中获取overrideSubscrbieUrl通知失败的map Map<NotifyListener, List<URL>>
        //然后移除listener
        Map<NotifyListener, List<URL>> notified = failedNotified.get(url);
        if (notified != null) {
            notified.remove(listener);
        }
    }

委托订阅请求给子类ZookeeperRegistry的doSubscribe实现

ZookeeperRegistry.doSubscribe(URI url,NotifyListener listener)

@Override
    protected void doSubscribe(final URL url, final NotifyListener listener) {
        try {
            //订阅所有数据,监控中心的订阅
            if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
                //获取根目录
                String root = toRootPath();
                //获取url对应的监听器集合
                ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
                if (listeners == null) {
                    //不存在,赋值新的
                    zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
                    listeners = zkListeners.get(url);
                }
                //获取listener的监听器
                ChildListener zkListener = listeners.get(listener);
                //zkListener为null说明需要创建新的
                if (zkListener == null) {
                    listeners.putIfAbsent(listener, new ChildListener() {
                        @Override
                        public void childChanged(String parentPath, List<String> currentChilds) {
                            //遍历当前节点,如果服务集合没有该节点,加入该节点,并订阅节点
                            for (String child : currentChilds) {
                                child = URL.decode(child);
                                if (!anyServices.contains(child)) {
                                    //添加到服务接口集合中
                                    anyServices.add(child);
                                    //订阅URL
                                    subscribe(url.setPath(child).addParameters(Constants.INTERFACE_KEY, child,
                                            Constants.CHECK_KEY, String.valueOf(false)), listener);
                                }
                            }
                        }
                    });
                    //再次获取listener对应的ChildListener
                    zkListener = listeners.get(listener);
                }
                //创建root永久节点
                zkClient.create(root, false);
                List<String> services = zkClient.addChildListener(root, zkListener);
                if (services != null && !services.isEmpty()) {
                    for (String service : services) {
                        service = URL.decode(service);
                        anyServices.add(service);
                        subscribe(url.setPath(service).addParameters(Constants.INTERFACE_KEY, service,
                                Constants.CHECK_KEY, String.valueOf(false)), listener);
                    }
                }
            } else {
                /**
                 * https://www.cnblogs.com/java-zhao/p/7632929.html 参考分析
                 */
                //处理消费者的订阅请求
                List<URL> urls = new ArrayList<URL>();
                //获取url的所有类型后

                //url
                //url(overrideSubscribeUrl):provider://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?
                // anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&
                // interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9544&side=provider&timestamp=1507643800076
                for (String path : toCategoriesPath(url)) {
                    ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
                    if (listeners == null) {
                        zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
                        listeners = zkListeners.get(url);
                    }
                    ChildListener zkListener = listeners.get(listener);
                    if (zkListener == null) {
                        listeners.putIfAbsent(listener, new ChildListener() {
                            @Override
                            public void childChanged(String parentPath, List<String> currentChilds) {
                                ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
                            }
                        });
                        zkListener = listeners.get(listener);
                    }
                    //创建持久化节点创建持久化节点/dubbo/com.alibaba.dubbo.demo.DemoService/configurators
                    zkClient.create(path, false);
                    //监听/dubbo/com.alibaba.dubbo.demo.DemoService/configurators节点
                    List<String> children = zkClient.addChildListener(path, zkListener);
                    if (children != null) {
                        urls.addAll(toUrlsWithEmpty(url, path, children));
                    }
                }
                notify(url, listener, urls);
            }
        } catch (Throwable e) {
            throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
        }
  • 1.根据url获取ConcurrentMap<NotifyListener, ChildListener>,没有就创建
  • 2.根据NotifyListener获取ChildListener,如果没有获取到创建一个匿名内部类ChildListener,主要是为了监听子节点configurators的变化
  • 3.创建path(如/dubbo/com.alibaba.dubbo.demo.DemoService/configurators)的持久化节点
  • 4.当前的zkClient添加对path子节点的监听器ChildListener
  • 5.最后调用notify方法

toCategoriesPath(url)

private String[] toCategoriesPath(URL url) {
        String[] categories;
        //代表所有类别
        if (Constants.ANY_VALUE.equals(url.getParameter(Constants.CATEGORY_KEY))) {
            categories = new String[]{Constants.PROVIDERS_CATEGORY, Constants.CONSUMERS_CATEGORY,
                    Constants.ROUTERS_CATEGORY, Constants.CONFIGURATORS_CATEGORY};
        } else {
            //获取url的category属性,没有则给定provider类型
            categories = url.getParameter(Constants.CATEGORY_KEY, new String[]{Constants.DEFAULT_CATEGORY});
        }
        String[] paths = new String[categories.length];
        for (int i = 0; i < categories.length; i++) {
            paths[i] = toServicePath(url) + Constants.PATH_SEPARATOR + categories[i];///dubbo/com.alibaba.dubbo.demo.DemoService/configurators
        }
        return paths;
    }

该方法通过获取url中名为category的属性,对于是catergory为*的做了下特殊处理返回providers/consumers/routers/configurators,循环category然后返回:/dubbo/com.alibaba.dubbo.demo.DemoService/configurators这样子的路径

AbstractZookeeperClient.addChildListener(String path,final ChildListener listener)

/**
     * 根据path从ConcurrentMap<String, ConcurrentMap<ChildListener, TargetChildListener>> childListeners
     * 获取ConcurrentMap<ChildListener, TargetChildListener>,没有就创建
     *
     * 根据ChildListener获取TargetChildListener,没有就创建,TargetChildListener是真正的监听path的子节点变化的监听器
     * createTargetChildListener(String path, final ChildListener listener):创建一个真正的用来执行当path节点的子节点发生变化时的逻辑
     *
     * addTargetChildListener(path, targetListener):将刚刚创建出来的子节点监听器订阅path的变化,这样之后,path的子节点发生了变化时
     * ,TargetChildListener才会执行相应的逻辑。而实际上TargetChildListener又会调用ChildListener的实现类的childChanged(String parentPath,
     * List<String> currentChilds)方法,而该实现类,正好是ZookeeperRegistry中实现的匿名内部类,
     * 在该匿名内部类的childChanged(String parentPath, List<String> currentChilds)方法中,
     * 调用了ZookeeperRegistry.notify(URL url, NotifyListener listener, List<URL> urls)
     *
     */
    @Override
    public List<String> addChildListener(String path, final ChildListener listener) {
        ConcurrentMap<ChildListener, TargetChildListener> listeners = childListeners.get(path);
        if (listeners == null) {
            childListeners.putIfAbsent(path, new ConcurrentHashMap<ChildListener, TargetChildListener>());
            listeners = childListeners.get(path);
        }
        TargetChildListener targetListener = listeners.get(listener);
        if (targetListener == null) {
            listeners.putIfAbsent(listener, createTargetChildListener(path, listener));
            targetListener = listeners.get(listener);
        }
        return addTargetChildListener(path, targetListener);
    }

AbstractZookeeperClient.createTargetChildListener(path, listener))

  • CuratorZookeeperClient
private class CuratorWatcherImpl implements CuratorWatcher {

        private volatile ChildListener listener;

        public CuratorWatcherImpl(ChildListener listener) {
            this.listener = listener;
        }

        public void unwatch() {
            this.listener = null;
        }

        @Override
        public void process(WatchedEvent event) throws Exception {
            if (listener != null) {
                String path = event.getPath() == null ? "" : event.getPath();
                listener.childChanged(path,
                        // if path is null, curator using watcher will throw NullPointerException.
                        // if client connect or disconnect to server, zookeeper will queue
                        // watched event(Watcher.Event.EventType.None, .., path = null).
                        StringUtils.isNotEmpty(path)
                                ? client.getChildren().usingWatcher(this).forPath(path)
                                : Collections.<String>emptyList());
            }
        }
    }
  • ZkClientZookeeperClient
​
public IZkChildListener createTargetChildListener(String path, final ChildListener listener) {
        return new IZkChildListener() {
            @Override
            public void handleChildChange(String parentPath, List<String> currentChilds)
                    throws Exception {
                listener.childChanged(parentPath, currentChilds);
            }
        };
    }

​

该方法用于创建zookeeper的path节点Listener的监听器监听节点变化,然后委托ChildListener处理path变化的事件!!!

AbstractZookeeperClient.addTargetChildListener(path, targetListener)

  • ZkclientZookeeperClient
@Override
    public List<String> addTargetChildListener(String path, final IZkChildListener listener) {
        return client.subscribeChildChanges(path, listener);
    }
  • CuratorZookeeperClient
@Override
public List<String> addTargetChildListener(String path, CuratorWatcher listener) {
    try {
        return client.getChildren().usingWatcher(listener).forPath(path);
    } catch (NoNodeException e) {
        return null;
    } catch (Exception e) {
        throw new IllegalStateException(e.getMessage(), e);
    }
}

该方法通过调用将path子节点与监听器绑定了起来并返回path的子节点。也就是时候当path下新增节点或者变化时最终会调用方法

@Override
                            public void childChanged(String parentPath, List<String> currentChilds) {
                                ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
                            }

ZookeeperRegistry.toUrlsWithEmpty(url,path,children)

/**
     *1.首先过滤出providers中与consumer匹配的providerUrl集合
     *2.如果providerUrl集合不为空,直接返回这个集合
     *3. 如果为空,首先从path中获取category,然后将consumer的协议换成empty,添加参数category=configurators
     *
     * @param consumer  provider://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?
     *                  anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0
     *                  &generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9544
     *                  &side=provider&timestamp=1507643800076
     * @param path      /dubbo/com.alibaba.dubbo.demo.DemoService/configurators
     * @param providers
     */
    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);
            URL empty = consumer.setProtocol(Constants.EMPTY_PROTOCOL).addParameter(Constants.CATEGORY_KEY, category);
            urls.add(empty);
        }
        return urls;
    }

    /**
     * 遍历providers集合
     * 1.首先判断provider是否包含"://"字符串
     * 2.然后匹配interfaceClass ,categroy,group,version,classifier
     * 3.匹配成功provider的加入到urls
     */
    private List<URL> toUrlsWithoutEmpty(URL consumer, List<String> providers) {
        List<URL> urls = new ArrayList<URL>();
        if (providers != null && !providers.isEmpty()) {
            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;
    }

该方法过滤出consumer(provider://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9544&side=provider&timestamp=1507643800076)匹配的服务配置URL(/dubbo/service/configurators)加入到urls中,用于重新暴露服务!

FailbackRegistry.notify(URL url,NotifyListener,List<URL> urls)

@Override
    protected void notify(URL url, NotifyListener listener, List<URL> urls) {
        if (url == null) {
            throw new IllegalArgumentException("notify url == null");
        }
        if (listener == null) {
            throw new IllegalArgumentException("notify listener == null");
        }
        try {
            doNotify(url, listener, urls);
        } catch (Exception t) {
            // Record a failed registration request to a failed list, retry regularly
            // 将失败的通知请求记录到失败列表,定时重试
            Map<NotifyListener, List<URL>> listeners = failedNotified.get(url);
            if (listeners == null) {
                failedNotified.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, List<URL>>());
                listeners = failedNotified.get(url);
            }
            listeners.put(listener, urls);
            logger.error("Failed to notify for subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
        }
    }

委托给doNotify方法,doNotify失败,将该url,NotifyListener,urls封装到failedNotified中,定时重试。FailebackRegistry的构造函数中启动了调度线程定时重试。

FailbackRegistry.doNotify(URL url,NotifyListener listenern,List<URL> urls)

protected void doNotify(URL url, NotifyListener listener, List<URL> urls) {
        super.notify(url, listener, urls);
    }

AbstractRegistry.notify(URL url, NotifyListener listener, List<URL> urls)

protected void notify(URL url, NotifyListener listener, List<URL> urls) {
        if (url == null) {
            throw new IllegalArgumentException("notify url == null");
        }
        if (listener == null) {
            throw new IllegalArgumentException("notify listener == null");
        }
        if ((urls == null || urls.isEmpty())
                && !Constants.ANY_VALUE.equals(url.getServiceInterface())) {
            logger.warn("Ignore empty notify urls for subscribe url " + url);
            return;
        }
        if (logger.isInfoEnabled()) {
            logger.info("Notify urls for subscribe url " + url + ", urls: " + urls);
        }
        Map<String, List<URL>> result = new HashMap<String, List<URL>>();
        //将匹配的urls按category分类保存到Map中
        //key-categroyName value-List<URL>
        for (URL u : urls) {
            if (UrlUtils.isMatch(url, u)) {
                String category = u.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
                List<URL> categoryList = result.get(category);
                if (categoryList == null) {
                    categoryList = new ArrayList<URL>();
                    result.put(category, categoryList);
                }
                categoryList.add(u);
            }
        }
        if (result.size() == 0) {
            return;
        }
        //获取url(overrideSubscribeUrl):provider://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?
        // anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&
        // interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9544&side=provider&timestamp=1507643800076
        //对应的通知过的YRL集合
        Map<String, List<URL>> categoryNotified = notified.get(url);
        if (categoryNotified == null) {
            notified.putIfAbsent(url, new ConcurrentHashMap<String, List<URL>>());
            categoryNotified = notified.get(url);
        }
        //遍历上述方法创建的map key-categroyName value-List<URL>集合
        for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
            //categroy
            String category = entry.getKey();
            //该分类对应的url
            List<URL> categoryList = entry.getValue();
            //放入到map中
            categoryNotified.put(category, categoryList);
            //保存到本地磁盘缓存中
            saveProperties(url);
            //调用传入的listener的notify方法(注意:这里调用的正是文章开头创建的overrideSubscribeListener实例的notify方法)
            listener.notify(categoryList);
        }
    }
  • 1.将匹配的urls按category分类保存到map,value是List<URL> urls(override://0.0.0/com.xxx.XXXservice?category这种的)也就是从/dubbo/service/configurators目录下的子节点过滤出符合url(provider://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false& interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9544&side=provider&timestamp=1507643800076)条件的urls(这些urls包含覆盖原服务的配置元数据)
  • 2.创建该url的那些urls(override://0.0.0/com.xxx.XXXservice?category这种的)配置通知了的map用于保存那些urls是对url成功通知的!
  • 3.遍历第一步创建的Map<"categoryName", List<URL>>将通知过的放入到notified中,然后保存传入的url(provider:xxx)到Properties properties,调用listener(OverrideListener类的)的notify方法重新暴露服务啊喂!

saveProperties(URL url)

private void saveProperties(URL url) {
        if (file == null) {
            return;
        }

        try {
            /**
             * 从将ConcurrentMap<URL, Map<String, List<URL>>> notified中将Map<String, List<URL>>拿出来,
             * 之后将所有category的list组成一串buf(以空格分隔)
             */
            StringBuilder buf = new StringBuilder();
            Map<String, List<URL>> categoryNotified = notified.get(url);
            if (categoryNotified != null) {
                for (List<URL> us : categoryNotified.values()) {
                    for (URL u : us) {
                        if (buf.length() > 0) {
                            buf.append(URL_SEPARATOR);
                        }
                        buf.append(u.toFullString());
                    }
                }
            }
            /**
             * 保存到properties文件中
             */
            properties.setProperty(url.getServiceKey(), buf.toString());
            long version = lastCacheChanged.incrementAndGet();
            //同步提交
            if (syncSaveFile) {
                doSaveProperties(version);
            } else {
                //异步提交
                registryCacheExecutor.execute(new SaveProperties(version));
            }
        } catch (Throwable t) {
            logger.warn(t.getMessage(), t);
        }
    }

保存notified数据到文件中;文件的key类似group/interface:version:比如在我的代码里group未配置,version也没配置此时就是:com.alibaba.dubbo.study.day01.xml.service.EchoService这样子。value就是configurators下的override://xxx这种,因为我并没有通过dubbo-admin给服务动态配置configurators,所以是这个样子:empty://169.254.22.149:20880/com.alibaba.dubbo.study.day01.xml.service.EchoService?addListener.1.callback=true&addListener.retries=2&anyhost=true&application=echo-provider&bean.name=com.alibaba.dubbo.study.day01.xml.service.EchoService&category=configurators&check=false&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=24724&side=provider&timestamp=1573200744509

doSavePropties(long version)

public void doSaveProperties(long version) {
        if (version < lastCacheChanged.get()) {
            return;
        }
        if (file == null) {
            return;
        }
        // Save
        try {
            File lockfile = new File(file.getAbsolutePath() + ".lock");
            if (!lockfile.exists()) {
                lockfile.createNewFile();
            }
            RandomAccessFile raf = new RandomAccessFile(lockfile, "rw");
            try {
                FileChannel channel = raf.getChannel();
                try {
                    FileLock lock = channel.tryLock();
                    if (lock == null) {
                        throw new IOException("Can not lock the registry cache file " + file.getAbsolutePath() + ", ignore and retry later, maybe multi java process use the file, please config: dubbo.registry.file=xxx.properties");
                    }
                    // Save
                    try {
                        if (!file.exists()) {
                            file.createNewFile();
                        }
                        FileOutputStream outputFile = new FileOutputStream(file);
                        try {
                            properties.store(outputFile, "Dubbo Registry Cache");
                        } finally {
                            outputFile.close();
                        }
                    } finally {
                        lock.release();
                    }
                } finally {
                    channel.close();
                }
            } finally {
                raf.close();
            }
        } catch (Throwable e) {
            if (version < lastCacheChanged.get()) {
                return;
            } else {
                registryCacheExecutor.execute(new SaveProperties(lastCacheChanged.incrementAndGet()));
            }
            logger.warn("Failed to save registry store file, cause: " + e.getMessage(), e);
        }
    }

保存到文件里的逻辑。

OverrideListener.notify(List<URL> urls)

/**
         * 已注册信息的列表始终不能为空,其含义与{@link com.alibaba.dubbo.registry.RegistryService#lookup(URL)}的返回值相同
         * @param urls The list of registered information , is always not empty, The meaning is the same as the return value of {@link com.alibaba.dubbo.registry.RegistryService#lookup(URL)}.
         */
        @Override
        public synchronized void notify(List<URL> urls) {
            logger.debug("original override urls: " + urls);
            List<URL> matchedUrls = getMatchedUrls(urls, subscribeUrl);
            logger.debug("subscribe url: " + subscribeUrl + ", override urls: " + matchedUrls);
            // No matching results
            //如果configurators的所有配置不需要应用于overrideSubscribeUrl,退出程序
            if (matchedUrls.isEmpty()) {
                return;
            }

            List<Configurator> configurators = RegistryDirectory.toConfigurators(matchedUrls);
            final Invoker<?> invoker;
            if (originInvoker instanceof InvokerDelegete) {
                invoker = ((InvokerDelegete<?>) originInvoker).getInvoker();
            } else {
                invoker = originInvoker;
            }
            dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&
            // generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=5279&side=provider&timestamp=1507723571451
            URL originUrl = RegistryProtocol.this.getProviderUrl(invoker);
            dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&
            // dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=5279&
            // side=provider&timestamp=1507723571451
            String key = getCacheKey(originInvoker);
            //根据key获取doLocalExport返回的Exporter
            ExporterChangeableWrapper<?> exporter = bounds.get(key);
            if (exporter == null) {
                logger.warn(new IllegalStateException("error state, exporter should not be null"));
                return;
            }
            //The current, may have been merged many times
            //获取exporter封装的invoker的url
            URL currentUrl = exporter.getInvoker().getUrl();
            //Merged with this configuration
            //使用configurators对originUrl进行merge操作
            URL newUrl = getConfigedInvokerUrl(configurators, originUrl);
            if (!currentUrl.equals(newUrl)) {
                重新将invoker暴露为exporter
                RegistryProtocol.this.doChangeLocalExport(originInvoker, newUrl);
                logger.info("exported provider url changed, origin url: " + originUrl + ", old export url: " + currentUrl + ", new export url: " + newUrl);
            }
        }

        private List<URL> getMatchedUrls(List<URL> configuratorUrls, URL currentSubscribe) {
            //配置的override://xxx 与overrideSubscribeUrl匹配下
            List<URL> result = new ArrayList<URL>();
            for (URL url : configuratorUrls) {
                URL overrideUrl = url;
                //如果/dubbo/service/configurators的url配置并没有category属性
                //并且为override协议
                if (url.getParameter(Constants.CATEGORY_KEY) == null
                        && Constants.OVERRIDE_PROTOCOL.equals(url.getProtocol())) {
                    //添加默认的category属性值为configurators
                    overrideUrl = url.addParameter(Constants.CATEGORY_KEY, Constants.CONFIGURATORS_CATEGORY);
                }
                //匹配!
                // Check whether url is to be applied to the current service
                if (UrlUtils.isMatch(currentSubscribe, overrideUrl)) {
                    result.add(url);
                }
            }
            return result;
        }

通过监听/dubbo/service/configurators下配置的类似override://xxx数据重新导出服务

/**
     * 对修改了url的invoker重新export
     *
     * @param originInvoker
     * @param newInvokerUrl
     */
    @SuppressWarnings("unchecked")
    private <T> void doChangeLocalExport(final Invoker<T> originInvoker, URL newInvokerUrl) {
        String key = getCacheKey(originInvoker);
        final ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
        if (exporter == null) {
            logger.warn(new IllegalStateException("error state, exporter should not be null"));
        } else {
            final Invoker<T> invokerDelegete = new InvokerDelegete<T>(originInvoker, newInvokerUrl);
            exporter.setExporter(protocol.export(invokerDelegete));
        }
    }

服务导出结束了。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值