源碼分析Dubbo配置規則機制(override協議)

在上篇在講解RegistryDirectory的時候,dubbo管理員能夠經過dubbo-admin管理系統在線上修改dubbo服務提供者的參數,最終將存儲在註冊中心的configurators catalog,而後通知RegistryDirectory更新服務提供者的URL中相關屬性,按照最新的配置,從新建立Invoker並銷燬原來的Invoker。 有關官方文檔關於動態改變配置(override協議)的詳細描述以下: 這裏寫圖片描述 這裏寫圖片描述 dubbo-admin 管理後臺,界面以下: 這裏寫圖片描述 這裏寫圖片描述api

當Dubbo管理人員在上述界面,選擇配置後點擊保存,會構建override:// url存入到註冊中心(configurators) catalog下,此時基於註冊中心發現服務提供者的監聽器(RegistryDirectory)會收到回調(notify)方法,接下來咱們再來看一下RegistryDirectory#notify方法。緩存

RegistryDirectory#notify架構

public synchronized void notify(List<url> urls) {        // @1
        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 &amp;&amp; configuratorUrls.size() &gt;0 ){       // @2
            this.configurators = toConfigurators(configuratorUrls);
        }
        // routers
        if (routerUrls != null &amp;&amp; routerUrls.size() &gt;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 &amp;&amp; localConfigurators.size() &gt; 0) {
            for (Configurator configurator : localConfigurators) {
                this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
            }
        }
        // providers
        refreshInvoker(invokerUrls);    // @3
}

因爲這個方法的實如今上一篇文章《源碼分析Dubbo服務發現機制(RegistryDirectory)》中詳細分析,故這裏只列出與本文章相關的關注點: 代碼@1:參數爲當前configurators目錄下全部的URL,例如:併發

urls: [override://0.0.0.0/com.wuys.frame.api.service.IUserService?category=configurators&amp;dynamic=false&amp;enabled=true&amp;timeout=10000, override://0.0.0.0/com.wuys.frame.api.service.IUserService?category=configurators&amp;dynamic=false&amp;enabled=true&amp;weight=200]。

代碼@2:將override url轉換爲List< Configurator>,是本節重點要討論的內容。 代碼@3:調用refreshInvoker方法,因爲這裏的invokerUrls爲空,此時會對原先的invoker立刻應用新的配置參數嗎?帶着這個疑問,咱們先看一下refreshInvoker是如何處理的,而後回頭重點分析代碼@2的實現細節。 關於refreshInvoker的實現,在上一篇源碼分析Dubbo服務註冊與發現機制RegistryDirectory)中也詳細分析過,這裏只是爲了求證一下:分佈式

if (invokerUrls.size() == 0 &amp;&amp; this.cachedInvokerUrls != null){
       invokerUrls.addAll(this.cachedInvokerUrls);
 } else {
      this.cachedInvokerUrls = new HashSet<url>();
            this.cachedInvokerUrls.addAll(invokerUrls);//緩存invokerUrls列表,便於交叉對比
      }
     if (invokerUrls.size() ==0 ){
          return;
     }
   // 省略部分代碼
}

從這裏看出,若是invokerUrls爲空,若是已緩存的服務提供者不爲空,則將已緩存的服務提供者加入到invokerUrls中,此時invokerUrls不爲空,則會從新用新的配置生成新的invoker,而後銷燬原先的invoker。ide

接下來重點分析Dubbo關於override協議的解析實現細節。高併發

一、dubbo關於override類圖

這裏寫圖片描述

  1. Configurator:協議配置接口,主要抽象出兩個接口方法:
  • URL getUrl():獲取配置URL。
  • URL configure(URL url):根據configureUrl來配置 URL url。
  1. AbstractConfigurator:協議配置抽象實現類(模板類)。
  2. AbsentConfigurator:absent配置器,其策略是,若是configureUrl存在的屬性,則不覆蓋。
  3. OverrideConfigurator:override配置器,其策略是,直接覆蓋屬性。

二、源碼分析OverrideConfigurator實現原理

2.1 源碼分析AbstractConfigurator#configure

AbstractConfigurator#configure源碼分析

  1. Configurator:協議配置接口,主要抽象出兩個接口方法:
  • URL getUrl():獲取配置URL。
  • URL configure(URL url):根據configureUrl來配置 URL url。
  1. AbstractConfigurator:協議配置抽象實現類(模板類)。
  2. AbsentConfigurator:absent配置器,其策略是,若是configureUrl存在的屬性,則不覆蓋。
  3. OverrideConfigurator:override配置器,其策略是,直接覆蓋屬性。

三、源碼分析OverrideConfigurator實現原理

3.1 源碼分析AbstractConfigurator#configure

AbstractConfigurator#configurepost

public URL configure(URL url) {
        if (configuratorUrl == null || configuratorUrl.getHost() == null
                || url == null || url.getHost() == null) {     // @1
            return url;
        }

        if (configuratorUrl.getPort() != 0) {          //  @2
            if (url.getPort() == configuratorUrl.getPort()) {
                return configureIfMatch(url.getHost(), url);      // @3
            }
        } else {
            if (url.getParameter(Constants.SIDE_KEY, Constants.PROVIDER).equals(Constants.CONSUMER)) {   // @4
                return configureIfMatch(NetUtils.getLocalHost(), url);// NetUtils.getLocalHost is the ip address consumer registered to registry.
            } else if (url.getParameter(Constants.SIDE_KEY, Constants.CONSUMER).equals(Constants.PROVIDER)) {   // @5
                return configureIfMatch(Constants.ANYHOST_VALUE, url);
            }
        }
        return url;
    }

代碼@1:若是configuratorUrl (配置URL)爲空host爲空,或url爲空或host爲空,則返回url。這裏參數的覆蓋方向 configuratorUrl ----> url。 代碼@2:若是configuratorUrl若是端口不爲空,則須要判斷url的端口,端口必須相同,才執行configuratorUrl配置url。 代碼@3,執行具體的配置操做,下文待分析。 代碼@四、@5:若是端口爲空,該配置URL(configuratorUrl)的類型要麼是針對消費者,要麼地址是0.0.0.0(任意)。this

若是url屬於服務消費者,host爲消費者的註冊IP地址,若是是服務提供者,則host爲0.0.0.0來配置。

3.2 源碼分析AbstractConfigurator#configureIfMatch

private URL configureIfMatch(String host, URL url) {
        if (Constants.ANYHOST_VALUE.equals(configuratorUrl.getHost()) || host.equals(configuratorUrl.getHost())) {
            String configApplication = configuratorUrl.getParameter(Constants.APPLICATION_KEY,
                    configuratorUrl.getUsername());
            String currentApplication = url.getParameter(Constants.APPLICATION_KEY, url.getUsername());
            if (configApplication == null || Constants.ANY_VALUE.equals(configApplication)
                    || configApplication.equals(currentApplication)) {
                Set<string> condtionKeys = new HashSet<string>();
                condtionKeys.add(Constants.CATEGORY_KEY);
                condtionKeys.add(Constants.CHECK_KEY);
                condtionKeys.add(Constants.DYNAMIC_KEY);
                condtionKeys.add(Constants.ENABLED_KEY);
                for (Map.Entry<string, string> entry : configuratorUrl.getParameters().entrySet()) {
                    String key = entry.getKey();
                    String value = entry.getValue();
                    if (key.startsWith("~") || Constants.APPLICATION_KEY.equals(key) || Constants.SIDE_KEY.equals(key)) {
                        condtionKeys.add(key);
                        if (value != null &amp;&amp; !Constants.ANY_VALUE.equals(value)
                                &amp;&amp; !value.equals(url.getParameter(key.startsWith("~") ? key.substring(1) : key))) {
                            return url;
                        }
                    }
                }
                return doConfigure(url, configuratorUrl.removeParameters(condtionKeys));
            }
        }
        return url;
    }

該方法主要實現的功能就是排除不能動態修改的屬性,不支持屬性主要包括:category、check、dynamic、enabled、還有以~開頭的屬性,而且若是~開頭的屬性,配置URL與原URL的值不相同,則不使用該配置URL重寫原URL。將配置URL(configuratorUrl)移除不支持屬性後,調用其子類的doConfigure方法覆蓋屬性,Dubbo默認支持以下覆蓋策略

  • override 直接覆蓋。
  • absent,若是原先存在該屬性的配置,則以原先配置的屬性值優先,若是原先沒有配置該屬性,則添加新的配置屬性。

總結一下:當在dubbo-admin(管理後臺)中建立一條override規則後,會首先存儲在註冊中心(zookeeper的指定目錄下${service}/configurators目錄下,此時基於註冊中心的事件機制,會通知相關監聽者(服務消費者),服務消費者收到最新的配置時,會根據最新的配置從新構建Invoker對象,而後銷燬原先的Invoker對象。


做者介紹:丁威,《RocketMQ技術內幕》做者,RocketMQ 社區佈道師,公衆號:中間件興趣圈 維護者,目前已陸續發表源碼分析Java集合、Java 併發包(JUC)、Netty、Mycat、Dubbo、RocketMQ、Mybatis等源碼專欄。能夠點擊連接:中間件知識星球,一塊兒探討高併發、分佈式服務架構,交流源碼。

</string,></string></string></url></configurator></router></url></url></url></url></url></url></url>

相關文章
相關標籤/搜索