在上一篇文章中,主要用了 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的屬性,例如 ApplicationConfig
、RegisterConfig
、Interface
以及ref
整個ServiceConfig,除了頂層的AbstractConfig是實現Serializable接口外,其他都是一個繼承體系,也就是說,ServiceConfig擁有上面4個類的所有字段信息(當然需要時可見的字段信息)。
翻閱代碼,發現 ApplicationConfig
和 RegisterConfig
都是在 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);
}
而 Interface
和 ref
則是在 ServiceConfig
中,其中, Interface只能爲接口,否則會報錯,而ref則是對應ServiceConfig的泛型變量。
export
從名字來看,就是暴露服務,那麼是一個怎麼樣的暴露過程呢?
整個export是一個 synchronized
方法,但是這是否會影響效率呢?不過這個方法調用頻率並不高,另一方面Java8以後虛擬機對 synchronized
關鍵字做了充分優化,這可能就是 Dubbo 選擇使用 方法級別原因?
裏面有兩個要做的:
- 檢查並填充配置
- 暴露服務
檢查配置是在 checkAndUpdateSubConfigs
中完成的
即檢查並且更新一些配置。
checkAndUpdateSubConfigs
在 checkAndUpdateSubConfigs
中,是一大段代碼,裏面包括對ServiceConfig中其他字段初始化,以及從註冊中心中獲取一些配置等,
本屆主要細說 completeCompoundConfigs
以及 startConfigCenter
completeCompoundConfigs
這個方法是放在第一,主要是綁定一些指定的配置,但是在ServiceConfig並沒有同步的配置,例如 application, module, registries, monitor, protocols, configCenter
等,但是裏面有優先級,在 registries, monitor
的設置上具有優先級,
即 ProviderConfig
> ModuleConfig
> ApplicationConfig
。
而這樣的優先級方法,則是通
== null
實現,即如果已經被設定了,就不會再次設定了。
startConfigCenter
在 startConfigCenter
裏面,首先嚐試設定 當前 AbstractInterfaceConfig
子類的值,即如果AbstractInterfaceConfig
中 configCenter
不爲空,就設置當前子類的 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;
}
爲什麼要將四個渠道配置都獲取下來呢?
主要有以下兩個原因:
- 獲取所有的配置,儘量保證如果寫了配置,那麼就需要填充上
- 形成 有序鏈,從而有利於後面的優先級獲取
而在後一端代碼:
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小吃街不迷路: