Provider初始化是一个较为复杂,但是逻辑清晰的一段代码。
上一篇文章细致的分析了 refresh
过程,refresh
是AbstractConfig的方法,即所有 它的子类,即所有 Config配置,都可以用到的方法。
主要功能有以下两点:
- 读取不同的配置
- 按照不同优先级设置配置到相应的
Config
本文以以下问题展开:
- Dubbo可以配置不同的 注册中心以及配置中心,那么,是在哪些逻辑下面 适配不同配置中心的呢?
- 目前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
信息,主要进行以下操作:
- 找不到任何
ProtocolConfig
, 则会新创键一个ProtocolConfig
,但是这一步不回指定Protocol
类型,后续会指定为dubbo
- 从外部化配置以及本实例中有配置
ProtocolConfig
,则会将其初始化并refresh
,而后放入到ServiceConfig
的tempProtocols
中
checkApplication
这一步则是检查是否需要设定 ApplicationConfig
信息。
- 尝试初始化
ApplicationConfig
,最后会判断是否有name
,通过判断name
来判断,如果没有,则会抛异常 - 在
ApplicationModel
中保存ApplicationConfig
的name
。 - 设置
SHUTDOWN_WAIT_SECONDS_KEY
和SHUTDOWN_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();
}
上面代码三个逻辑:
- 从当前 ReferenceConfig中加载 registries信息,并设置为
RegistryConfig
- 如果上一步中,没有发现任何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);
}
- 依次判断各个注册中心是否有用,没有用 即
!registryConfig.isValid()
则会抛出异常 - 尝试从注册中心中读取配置,即将注册中心作为配置中心,并重新刷新整个配置读取。
将注册中心变为默认的配置中心,在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
,这个方法主要就是读取配置中心的配置。
主要有以下几个作用:
- 通过SPI机制不同类型配置中心配置
- 读取配置数据到Environment的
externalConfigurationMap
和appExternalConfigurationMap
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
读取配置,通过传入给定的 key
和 group
。 而前一行代码中,相应的 动态配置类,从而执行不同的配置中心获取逻辑:
目前dubbo(2.7.2)支持以下集中配置中心:
- Apollo:是携程框架部门研发的开源配置管理中心 。Apollo GitHub 介绍
- Consul:是Spring Cloud的一款分布式配置中心。Consul GitHub 介绍
- etcd:分布式可靠的键值对存储。etcd GitHub介绍
- Nacos:阿里巴巴 开源的一款分布式注册中心。 Nacos GitHub 介绍
- Zookeeper: Apache 一款开源的 配置中心。Zookeeper GitHub介绍
而 NopDynamicConfiguration
只是一个空架子,只是实现 DynamicConfiguration
但是没有实现任何方法。以前是作为默认注册中心,即没有声明配置中心时候以它作为默认配置中心。
checkMetadataReport
用于设置 MetadataReportConfig
,尝试从配置中加载其配置,就算 MetadataReportConfig
不可用(即 unvalid),也只会报警告。
余下
余下来的checkAndUpdateSubConfigs
有以下几个点:
- 对是否为
GenericService
进行适配(GenricService:泛化调用 是一种 Dubbo 提供的特殊角色,不需要引入jar包,直接通过GenericService进行调用,用于服务测试以及API服务网关,后面文章细分析) - 检查
ref
和interface
是否为同一个类别 - 检查Stub和 Mock属性,从而实现不同逻辑调用
以上几个点需要单篇文章分析,希望关注后面文章。
总结
本文详细讲解了 Dubbo 的 服务提供者外部化配置初始化过程,主要包括以下:
startConfigCenter
中其他剩下的配置检查以及加载过程- SPI 方式配置中心加载(SPI 后面文章分析)
- 获取到的加载的数据的设值
下一篇文章则进入 Provider初始化过程最后一个阶段,服务暴露。是不是感觉终于要进入正题的感觉了??哈哈别急
觉得博主写的有用,不妨关注博主公众号: 六点A君。
哈哈哈,Dubbo小吃街不迷路: