Dubbo基礎(二)- Provider的初始化過程A:配置讀取過程refresh過程

在上一篇文章中,主要用了 dubbo-samples的一個簡單例子,來跑通了Dubbo例子,那麼這一篇文章中,將以上一篇文章爲基礎,研究下Dubbo Provider是如何跑起來的。

ServiceConfig

以下思路是先從設置字段變量,然後再關注export方法入手:

變量設置

先把代碼找過來:

        ServiceConfig<HelloService> service = new ServiceConfig<>();
        service.setApplication(new ApplicationConfig("dubbo-provider"));
        service.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
        service.setInterface(HelloService.class);
        service.setRef(new HelloServiceImpl());
        service.export();

代碼主要圍繞ServiceConfig 展開,上面代碼就是設置一些ServiceConfig的屬性,例如 ApplicationConfigRegisterConfigInterface 以及ref
在這裏插入圖片描述
整個ServiceConfig,除了頂層的AbstractConfig是實現Serializable接口外,其他都是一個繼承體系,也就是說,ServiceConfig擁有上面4個類的所有字段信息(當然需要時可見的字段信息)。

翻閱代碼,發現 ApplicationConfigRegisterConfig 都是在 AbstractInterfaceConfig中,但是他們卻又不同,Application只能有一個,而RegisterConfig則是用List存儲的,所以也驗證了Dubbo註冊中心可以有多個。

	// 設置 application
    public void setApplication(ApplicationConfig application) {
        ConfigManager.getInstance().setApplication(application);
        this.application = application;
    }

// 設置 RegisterConfig
    public void setRegistry(RegistryConfig registry) {
        List<RegistryConfig> registries = new ArrayList<RegistryConfig>(1);
        registries.add(registry);
        setRegistries(registries);
    }

Interfaceref 則是在 ServiceConfig 中,其中, Interface只能爲接口,否則會報錯,而ref則是對應ServiceConfig的泛型變量。

export

從名字來看,就是暴露服務,那麼是一個怎麼樣的暴露過程呢?
整個export是一個 synchronized 方法,但是這是否會影響效率呢?不過這個方法調用頻率並不高,另一方面Java8以後虛擬機對 synchronized 關鍵字做了充分優化,這可能就是 Dubbo 選擇使用 方法級別原因?
裏面有兩個要做的:

  1. 檢查並填充配置
  2. 暴露服務

檢查配置是在 checkAndUpdateSubConfigs 中完成的
即檢查並且更新一些配置。

checkAndUpdateSubConfigs

checkAndUpdateSubConfigs 中,是一大段代碼,裏面包括對ServiceConfig中其他字段初始化,以及從註冊中心中獲取一些配置等,
本屆主要細說 completeCompoundConfigs 以及 startConfigCenter

completeCompoundConfigs

這個方法是放在第一,主要是綁定一些指定的配置,但是在ServiceConfig並沒有同步的配置,例如 application, module, registries, monitor, protocols, configCenter等,但是裏面有優先級,在 registries, monitor 的設置上具有優先級,
ProviderConfig > ModuleConfig > ApplicationConfig
而這樣的優先級方法,則是通
== null 實現,即如果已經被設定了,就不會再次設定了。

startConfigCenter

startConfigCenter裏面,首先嚐試設定 當前 AbstractInterfaceConfig子類的值,即如果AbstractInterfaceConfigconfigCenter不爲空,就設置當前子類的 configCenter 值, 在本例中就是 ServiceConfig的
然後,如果 ConfigCenter 不爲空,那麼就嘗試 refresh 一下,從而從新獲取配置。
以及 執行 prepareEnviroment 方法

最後,將會由配置管理器 ConfigManager 執行 refreshAll 方法,而這個方法 將 ApplicationConfig, MonitorConfig, ModuleConfig, ProtocolConfig, RegistryConfig, ProviderConfig, ConsumerConfig 會都執行 refresh 方法。

這裏只執行 7 個 config的refresh方法,而不是執行所有的 refresh方法

因爲 configCenter 的初始化被分開了,並且當 configCenter != null 時候,除了 執行 this.configCenter.refresh(); 還會執行 prepareEnvironment, 而 prepareEnviroment則是進行一些前期環境檢查工作。而對於 可配置的 多類型註冊中心,則是在這裏面進行設定的。

ConfigManager中,

ConfigManager

ConfigManager裏面,主要 負責維護以下幾個config

    private ApplicationConfig application;
    private MonitorConfig monitor;
    private ModuleConfig module;
    private ConfigCenterConfig configCenter;

    private Map<String, ProtocolConfig> protocols = new ConcurrentHashMap<>();
    private Map<String, RegistryConfig> registries = new ConcurrentHashMap<>();
    private Map<String, ProviderConfig> providers = new ConcurrentHashMap<>();
    private Map<String, ConsumerConfig> consumers = new ConcurrentHashMap<>();

從上面定義可知, 一個應用可以有多個 ProtocolConfig, RegistryConfig, ProviderConfig, ConsumerConfig 等配置,而其他的只能有一個。
並且 ConfigManager屬於餓漢式的單例模式,有興趣可以看: 單例模式之餓漢模式(立即加載)

refresh

refresh方法主要是獲取當前config下最新配置,將系統配置,或者外部化配置設置到config中。

    public void refresh() {
        try { 
        // 獲取 多個混合配置
            CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());
            InmemoryConfiguration config = new InmemoryConfiguration(getPrefix(), getId());
            // 將當前類的 信息放入到配置中
            config.addProperties(getMetaData());
            // 看 isConfig的順序,從而加載不同配置
            if (Environment.getInstance().isConfigCenterFirst()) {
                // The sequence would be: SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
                compositeConfiguration.addConfiguration(3, config);
            } else {
                // The sequence would be: SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration
                compositeConfiguration.addConfiguration(1, config);
            }

            // 循環,填充參數
            Method[] methods = getClass().getMethods();
            for (Method method : methods) {
                if (MethodUtils.isSetter(method)) {
                    try {
                        String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
                        // isTypeMatch() is called to avoid duplicate and incorrect update, for example, we have two 'setGeneric' methods in ReferenceConfig.
                        if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
                            method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
                        }
                    } catch (NoSuchMethodException e) {
                        logger.info("Failed to override the property " + method.getName() + " in " +
                                this.getClass().getSimpleName() +
                                ", please make sure every property has getter/setter method provided.");
                    }
                }
            }
        } catch (Exception e) {
            logger.error("Failed to override ", e);
        }
    }

在上一段代碼中,首先會 Environment.getInstance().getConfiguration(getPrefix(), getId()); 會去獲取 4個渠道里面所有的關於 getPrefix, getId 的配置,如下在 Environment.getConfiguration中代碼:

    public CompositeConfiguration getConfiguration(String prefix, String id) {
        CompositeConfiguration compositeConfiguration = new CompositeConfiguration();
        // Config center has the highest priority
        compositeConfiguration.addConfiguration(this.getSystemConfig(prefix, id));
        compositeConfiguration.addConfiguration(this.getAppExternalConfig(prefix, id));
        compositeConfiguration.addConfiguration(this.getExternalConfig(prefix, id));
        compositeConfiguration.addConfiguration(this.getPropertiesConfig(prefix, id));
		// 最後封裝返回複合配置
        return compositeConfiguration;
    }

爲什麼要將四個渠道配置都獲取下來呢?
主要有以下兩個原因:

  1. 獲取所有的配置,儘量保證如果寫了配置,那麼就需要填充上
  2. 形成 有序鏈,從而有利於後面的優先級獲取

而在後一端代碼:

InmemoryConfiguration config = new InmemoryConfiguration(getPrefix(), getId());
            config.addProperties(getMetaData());

則是爲了將metaData數據加入,而在 getMetaData 中,則 使用反射,將 遍歷所有的 get*的獲取基本類型方法,從而獲取已有的配置裏面的所有的字段值。
另一方面,如果有 getParameters,也同樣回獲取 getParameters的鍵值對,將其一併返回,這樣依賴在
另一方面最後以Map返回。

配置優先級

配置優先級關鍵就是在以下代碼:

            if (Environment.getInstance().isConfigCenterFirst()) {
                // The sequence would be: SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
                compositeConfiguration.addConfiguration(3, config);
            } else {
                // The sequence would be: SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration
                compositeConfiguration.addConfiguration(1, config);
            }

由於上一小節中,瞭解到 compositeConfiguration 中,會包括所有地方的配置,主要是4個地方:
SystemConfiguration ,AppExternalConfiguration , ExternalConfiguration , PropertiesConfiguration
而剛剛從本類中的配置獲取到的 InmemoryConfiguration,則是由配置,決定 它的優先級,默認下是 配置中心優先級要高於 外部化配置

設置變量

最後,如何來設定變量呢?
即拿到了配置,就到了該要設定配置的時候了,Dubbo給了一個循環,將 set* 方法拿到,再有上一步獲取的配置去配置值。

            for (Method method : methods) {
                if (MethodUtils.isSetter(method)) {
                    try {
                    // 獲取值,
                        String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
                        // isTypeMatch() is called to avoid duplicate and incorrect update, for example, we have two 'setGeneric' methods in ReferenceConfig.
                        if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
                            method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
                        }
                    } catch (NoSuchMethodException e) {
                        logger.info("Failed to override the property " + method.getName() + " in " +
                                this.getClass().getSimpleName() +
                                ", please make sure every property has getter/setter method provided.");
                    }
                }
            }

這一段代碼中,主要通過 compositeConfiguration.getString(extractPropertyName(getClass(), method)) 獲取配置,而裏面則是遍歷 compositeConfiguration 的 配置list,而由於優先級先後,所以就有了上一步的優先級定義。

當以配置中心優先時候,當前配置字段生效順序是:
SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
而當不是配置中心優先時候,配置生效順序則爲:
SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration

而 AbstractConfig,則是程序員自己在程序中,或者是在yml或者xml等配置文件中配置。

爲了避免篇幅太大,導致閱讀疲勞,下一篇則繼續分析 checkAndUpdateSubConfigs 其他部分,以及結合 ConfigCenter 示例 看Dubbo具體如何操作配置優先級的。

關注博主公衆號: 六點A君。
哈哈哈,Dubbo小吃街不迷路:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章