Dubbo基础(三)- Provider的初始化过程B:外部化配置初始化过程

Provider初始化是一个较为复杂,但是逻辑清晰的一段代码。
上一篇文章细致的分析了 refresh过程,refresh 是AbstractConfig的方法,即所有 它的子类,即所有 Config配置,都可以用到的方法。
主要功能有以下两点:

  1. 读取不同的配置
  2. 按照不同优先级设置配置到相应的Config

本文以以下问题展开:

  1. Dubbo可以配置不同的 注册中心以及配置中心,那么,是在哪些逻辑下面 适配不同配置中心的呢?
  2. 目前Dubbo 支持那些配置中心?

以上问题看完文章后相信大家就可以清楚,若有疑问,关注博主公众号:六点A君,回复标题获取最新答案><

前言 startConfigCenter

怎么又是startConfigCenter?上篇文章不是分析过了?
由于在一开始例子中, 并没有配置 配置中心,所以并不会执行:

            this.configCenter.refresh();
            prepareEnvironment();

上一句就是refresh过程,而读取外部化配置则主要在 prepareEnvironment 中。

默认配置中心

上小节分析了,由于在第一篇例子中,分析到如果没有配置中心,Dubbo 会给你设置默认配置中心。

还是接着 checkAndUpdateSubConfigs 往下。

checkDefault

checkDefault 主要是 实例化 ProviderConfig过程,如果 ProviderConfig 不存在,那么就新建一个默认的,并执行 refresh 方法。

checkProtocol

checkProtocol 则主要是实例化 ProtocolConfig, 这里会判断是否有 providerConfig,如果有的化则会会设置为 ProviderConfig 的配置,但是如果在ServiceConfig中自己有配置 ProtocolConfig 信息,则会使用 ServiceConfig 信息:

    private void checkProtocol() {
        if (CollectionUtils.isEmpty(protocols) && provider != null) {
            setProtocols(provider.getProtocols());
        }
     	// 如果ServiceConfig有,则进一步覆盖
        convertProtocolIdsToProtocols();
    }

convertProtocolIdsToProtocols 中,会先去读取 外部化配置 的所有的 protocol 信息,主要进行以下操作:

  1. 找不到任何ProtocolConfig, 则会新创键一个 ProtocolConfig ,但是这一步不回指定 Protocol 类型,后续会指定为 dubbo
  2. 从外部化配置以及本实例中有配置 ProtocolConfig ,则会将其初始化并refresh,而后放入到 ServiceConfigtempProtocols
checkApplication

这一步则是检查是否需要设定 ApplicationConfig 信息。

  1. 尝试初始化 ApplicationConfig,最后会判断是否有 name,通过判断 name 来判断,如果没有,则会抛异常
  2. ApplicationModel 中保存 ApplicationConfigname
  3. 设置 SHUTDOWN_WAIT_SECONDS_KEYSHUTDOWN_WAIT_KEY 的值到 System.proterty中,这两个属性用于Dubbo优雅停机,相关研究后续文章中体现。
checkRegistry()

如果不是 injvm的应用,那么就会调用检查一遍 RegistryConfig,injvm特性主要是协议有且仅有一个,且使用的是 injvm

    protected void checkRegistry() {
    	// 从当前 ReferenceConfig中加载 config信息
        loadRegistriesFromBackwardConfig();
		// 读取外部化配置的 
        convertRegistryIdsToRegistries();
		// 判断是否有用
        for (RegistryConfig registryConfig : registries) {
            if (!registryConfig.isValid()) {
                throw new IllegalStateException("No registry config found or it's not a valid config! " +
                        "The registry config is: " + registryConfig);
            }
        }
		// 将读取到的配置中心,进一步获取,并
        useRegistryForConfigIfNecessary();
    }

上面代码三个逻辑:

  1. 从当前 ReferenceConfig中加载 registries信息,并设置为 RegistryConfig
  2. 如果上一步中,没有发现任何registries信息,就会尝试从 ** 外部化配置** 中获取
// 判断 registries 是否为空
        if (StringUtils.isEmpty(registryIds) && CollectionUtils.isEmpty(registries)) {
            Set<String> configedRegistries = new HashSet<>();
            // 从外部化中获取
            configedRegistries.addAll(getSubProperties(Environment.getInstance().getExternalConfigurationMap(),
                    REGISTRIES_SUFFIX));
            configedRegistries.addAll(getSubProperties(Environment.getInstance().getAppExternalConfigurationMap(),
                    REGISTRIES_SUFFIX));

            registryIds = String.join(COMMA_SEPARATOR, configedRegistries);
        }
  1. 依次判断各个注册中心是否有用,没有用 即 !registryConfig.isValid() 则会抛出异常
  2. 尝试从注册中心中读取配置,即将注册中心作为配置中心,并重新刷新整个配置读取。
    将注册中心变为默认的配置中心,在 useRegistryForConfigIfNecessary 有所体现:
    private void useRegistryForConfigIfNecessary() {
        registries.stream().filter(RegistryConfig::isZookeeperProtocol).findFirst().ifPresent(rc -> {
            // we use the loading status of DynamicConfiguration to decide whether ConfigCenter has been initiated.
            Environment.getInstance().getDynamicConfiguration().orElseGet(() -> {
            // 获取单例的 配置管理器
                ConfigManager configManager = ConfigManager.getInstance();
                // 窗即一个配置中心
                ConfigCenterConfig cc = configManager.getConfigCenter().orElse(new ConfigCenterConfig());
                // 设置协议
                cc.setProtocol(rc.getProtocol());
                // 设置地址
                cc.setAddress(rc.getAddress());
                // 设置优先级
                cc.setHighestPriority(false);
                // 设置配置中心
                setConfigCenter(cc);
                // 从配置中心刷新所有配置
                startConfigCenter();
                return null;
            });
        });
    }

这里有一个细节,在将 registry 变为默认的 ConfigCenter 时候,将优先级设为false,这样就不会让这个配置中心去覆盖本地的配置。

    // If the Config Center is given the highest priority, it will override all the other configurations
    private Boolean highestPriority = true;
prepareEnvironment

startConfigCenter 会有这么一段:

       if (this.configCenter != null) {
            // TODO there may have duplicate refresh
            this.configCenter.refresh();
            prepareEnvironment();
        }

当配置中心不为空时候,就会去执行 prepareEnvironment,这个方法主要就是读取配置中心的配置。
主要有以下几个作用:

  1. 通过SPI机制不同类型配置中心配置
  2. 读取配置数据到Environment的 externalConfigurationMapappExternalConfigurationMap
        if (configCenter.isValid()) {
            if (!configCenter.checkOrUpdateInited()) {
                return;
            }
            // 加载 配置中心
            DynamicConfiguration dynamicConfiguration = getDynamicConfiguration(configCenter.toUrl());
            // 从配置中心获取 configFile 配置
            String configContent = dynamicConfiguration.getConfigs(configCenter.getConfigFile(), configCenter.getGroup());

            String appGroup = application != null ? application.getName() : null;
            String appConfigContent = null;
            // 从配置中心获取 appConfigFile 配置
            if (StringUtils.isNotEmpty(appGroup)) {
                appConfigContent = dynamicConfiguration.getConfigs
                        (StringUtils.isNotEmpty(configCenter.getAppConfigFile()) ? configCenter.getAppConfigFile() : configCenter.getConfigFile(),
                         appGroup
                        );
            }
            try {
            // 将获取到的String 类型的 url类型 数据转化为 key value
                Environment.getInstance().setConfigCenterFirst(configCenter.isHighestPriority());
                Environment.getInstance().updateExternalConfigurationMap(parseProperties(configContent));
                Environment.getInstance().updateAppExternalConfigurationMap(parseProperties(appConfigContent));
            } catch (IOException e) {
                throw new IllegalStateException("Failed to parse configurations from Config Center.", e);
            }
        }

上述代码中标识,如果ConfigCenter没有appConfigFile,则会使用 configFile 替代 appConfigFile
最终,从配置文件中获取到的,是 String类型,一行一个数据,最后用 Properties 再将 其读出,执行
updateExternalConfigurationMap或者 updateAppExternalConfigurationMap 进行更新。

那么往回看一下,dynamicConfiguration是怎么加载配置的呢?

在 getDynamicConfiguration(URL url) 中:

		// SPI 方式,加载不同的 ConfigurationFactory
        DynamicConfigurationFactory factories = ExtensionLoader
                .getExtensionLoader(DynamicConfigurationFactory.class)
                .getExtension(url.getProtocol());
        // 获取 动态 配置
        DynamicConfiguration configuration = factories.getDynamicConfiguration(url);
        Environment.getInstance().setDynamicConfiguration(configuration);
        return configuration;

上面即以 SPI 方式加载配置,根据 Protocol的类型,加载不同 扩展配置项。

得到配置以后,通过 getConfigs 读取配置,通过传入给定的 keygroup。 而前一行代码中,相应的 动态配置类,从而执行不同的配置中心获取逻辑:
在这里插入图片描述
目前dubbo(2.7.2)支持以下集中配置中心:

  1. Apollo:是携程框架部门研发的开源配置管理中心 。Apollo GitHub 介绍
  2. Consul:是Spring Cloud的一款分布式配置中心。Consul GitHub 介绍
  3. etcd:分布式可靠的键值对存储。etcd GitHub介绍
  4. Nacos:阿里巴巴 开源的一款分布式注册中心。 Nacos GitHub 介绍
  5. Zookeeper: Apache 一款开源的 配置中心。Zookeeper GitHub介绍

NopDynamicConfiguration 只是一个空架子,只是实现 DynamicConfiguration 但是没有实现任何方法。以前是作为默认注册中心,即没有声明配置中心时候以它作为默认配置中心。

checkMetadataReport

用于设置 MetadataReportConfig,尝试从配置中加载其配置,就算 MetadataReportConfig 不可用(即 unvalid),也只会报警告。

余下

余下来的checkAndUpdateSubConfigs 有以下几个点:

  1. 对是否为 GenericService 进行适配(GenricService:泛化调用 是一种 Dubbo 提供的特殊角色,不需要引入jar包,直接通过GenericService进行调用,用于服务测试以及API服务网关,后面文章细分析)
  2. 检查refinterface 是否为同一个类别
  3. 检查Stub和 Mock属性,从而实现不同逻辑调用

以上几个点需要单篇文章分析,希望关注后面文章。

总结

本文详细讲解了 Dubbo 的 服务提供者外部化配置初始化过程,主要包括以下:

  1. startConfigCenter中其他剩下的配置检查以及加载过程
  2. SPI 方式配置中心加载(SPI 后面文章分析)
  3. 获取到的加载的数据的设值

下一篇文章则进入 Provider初始化过程最后一个阶段,服务暴露。是不是感觉终于要进入正题的感觉了??哈哈别急

觉得博主写的有用,不妨关注博主公众号: 六点A君。
哈哈哈,Dubbo小吃街不迷路:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值