Dubbo源码解析-Provider端监听注册中心动态配置原理

上篇我们介绍了provider服务暴露源码,地址如下

Dubbo源码解析-Provider服务暴露Export源码解析_dubbo exporter-CSDN博客

        本文主要针Dubbo服务端注册中心节点,实现动态配置变更原理,从dubbo源码角度进行解析。

        Dubbo 服务端动态配置原理比较简单,也是面试过程中比较常问的技术问题,大家可以好好仔细读一下本文。有疑问欢迎留言。

        接着说明,读Dubbo源码最好是先对Spring源码有一定的了解。如果大家需要,我也可以针对Spring框架做一系列源码的解读专栏。

         不过不用担心,如果需要Spring的源码知识,文章中也会进行Spring源码铺垫介绍的。

        如果内容中有没描述清楚的,或者大家在阅读源代码有疑问的,欢迎留言,看到就会及时回复。

        为了更清楚的分析解释源码,源代码中部分不重要的内容可能会删减,保留重要内容方便大家理解。

        我们知道,Dubbo对每个服务的配置,会写入到服务节点对应的configurators节点,在服务端服务启动后,会注册监听时间,来监听/dubbo/com.*.*/configurators/节点变更。从而实现动态刷新本地服务配置功能。具体过程我们接下来详细介绍一下。

主要内容

  • 服务端什么时候订阅的
  • 服务端订阅事件做了什么

服务端如何订阅的

        Dubbo对每个服务的配置,会写入到服务节点对应的configurators节点,在服务端服务启动后,会注册监听时间,来监听/dubbo/com.*.*/configurators/节点变更。具体流程可以参考Dubbo源码解析-Provider服务暴露Export源码解析_dubbo exporter-CSDN博客

服务端订阅事件

        服务端在启动的过程中建立了一个notify的映射关系CuratorWatch.press->ChildListener.childChange()->AbstractRegistry.notify()。完成override事件对主机属性的覆盖和对客户端代理的生成。

流程概述:

  1. verride协议中修改或者添加的属性合并到originUrl中,生成新的URL
  2. 比较新的URL和当前URL是否相同
  3. 不相同则重新完成服务暴露:其实就是创建一个新的DubboExport对象。
  4. 服务端只做了一件事,根据新的URL,创建新的DubboExport对象。

具体流程

  1. CuratorWatc.precess调用到ChildListener.childChanged()
  2. AbstractRegistry.notify(URL url, NotifyListener listener, List<URL> urls).url:provider协议URL,urls为empty协议或者override协议地址:configurators节点下的
    1. 遍历urls
    2. 判断和url是否匹配,主要是判断目录是否相等
    3. result = new HashMap<String, List<URL>>().创建新的目录和override的list的映射
    4. 覆盖notified全局变量,ConcurrentMap<URL, Map<String, List<URL>>>prividerUrl和configurators和override映射
    5. .saveProperties(url):保存本地文件,把override协议列表保存到本地缓存文件
      1. 文件properties,key为服务名称,value为空格分割的orverride协议地址
      2. 文件地址:用户目录+/.dubbo/dubbo-registry-应用名称—服务地址.cache
      3. 保存文件的目的:服务在启动的时候可以优先从本地加载。AbstractRegistry构造函数中调用
        1. loadProperties():加载缓存文件中的override配置,不需要zookeeper也可以完成属性覆盖
        2. notify(url.getBackupUrls()):触发覆盖
    6. listener.notify(categoryList):调用overrideListener,privider端口,则只修改dubboExport
      1. 将override的URL集合,专为List<Configurator>
      2. 获取dubbo协议地址
        1. URL originUrl = RegistryProtocol.this.getProviderUrl(invoker);
      3. 当前dubboURL
        1. URL currentUrl = exporter.getInvoker().getUrl();
      4. 把override协议中的属性合并到dubbo协议中Configurator中
        1. URL newUrl = getConfigedInvokerUrl(configurators, originUrl);
      5. 如果当前url和新url不同,则重新创建DubboExport对象,持有inoker执行链,invoker中有新的url
        1. RegistryProtocol.this.doChangeLocalExport(originInvoker, newUrl);

         总结:

  1. verride协议中修改或者添加的属性合并到originUrl中,生成新的URL
  2. 比较新的URL和当前URL是否相同
  3. 不相同则重新完成服务暴露:其实就是创建一个新的DubboExport对象。

                
      Override,Configurators在服务端触发
                    .notify方法,服务端只做了一件事,根据新的URL,创建新的DubboExport对象。
 

源码解析

1.AbstractRegistry.notify

protected void notify(URL url, NotifyListener listener, List<URL> urls) {
        
        Map<String, List<URL>> result = new HashMap<String, 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>();
                    //创建新的目录和override的list的映射
                    result.put(category, categoryList);
                }
                categoryList.add(u);
            }
        }
        if (result.size() == 0) {
            return;
        }
        //获取缓存中的override协议的list
        Map<String, List<URL>> categoryNotified = notified.get(url);
        if (categoryNotified == null) {
            notified.putIfAbsent(url, new ConcurrentHashMap<String, List<URL>>());
            categoryNotified = notified.get(url);
        }
        for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
            String category = entry.getKey();
            List<URL> categoryList = entry.getValue();
            //覆盖notified缓存中的
            categoryNotified.put(category, categoryList);
            //把override协议列表保存到本地缓存文件中
            saveProperties(url);
            //事件触发核心
            listener.notify(categoryList);
        }
    }

2.RegistryProtocol.OverrideListener.Notify

public synchronized void notify(List<URL> urls) {
            

            //根据覆盖override url生成Configurator对象
            List<Configurator> configurators = RegistryDirectory.toConfigurators(matchedUrls);

            
            //The origin invoker
            URL originUrl = RegistryProtocol.this.getProviderUrl(invoker);
            String key = getCacheKey(originInvoker);
            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
            URL currentUrl = exporter.getInvoker().getUrl();
            //Merged with this configuration
            //把override协议中修改或者添加的属性合并到originUrl中
            URL newUrl = getConfigedInvokerUrl(configurators, originUrl);
            //如果新旧url不相等
            if (!currentUrl.equals(newUrl)) {
                //这里URL对象改了,需要重新创建DubboExporter对象,覆盖之前的那个对象
                RegistryProtocol.this.doChangeLocalExport(originInvoker, newUrl);
                logger.info("exported provider url changed, origin url: " + originUrl + ", old export url: " + currentUrl + ", new export url: " + newUrl);
            }
        }

总结:上面内容中,每个从业务流程和源码角度进行了详细分析,如果大家有疑问或者对文章排版任何方面有建议都可以留言评论,看到都会及时回复大家。

知识总结,分享不易,全文手敲,欢迎大家关注点赞评论收藏。

  • 58
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜鸟long

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值