Provider初始化是一個較爲複雜,但是邏輯清晰的一段代碼。
上一篇文章細緻的分析了 refresh
過程,refresh
是AbstractConfig的方法,即所有 它的子類,即所有 Config配置,都可以用到的方法。
主要功能有以下兩點:
- 讀取不同的配置
- 按照不同優先級設置配置到相應的
Config
Dubbo可以配置不同的 註冊中心以及配置中心,那麼,是在哪些邏輯下面 適配不同配置中心的呢?
本篇文章將揭曉這一謎題。
前言 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小吃街不迷路: