Netflix Eureka原始碼分析(3)——listener(EurekaBootStrap監聽類)分析
web.xml中的listener:
<listener>
<listener-class>com.netflix.eureka.EurekaBootStrap</listener-class>
</listener>
在eureka-core包下找到EurekaBootStrap監聽類,下邊監聽器EurekaBootStrap的執行初始化的方法,是contextInitialized()方法,這個方法就是整個eureka-server啟動初始化的一個入口。
@Override public void contextInitialized(ServletContextEvent event) { try { initEurekaEnvironment(); initEurekaServerContext(); ServletContext sc = event.getServletContext(); sc.setAttribute(EurekaServerContext.class.getName(), serverContext); } catch (Throwable e) { logger.error("Cannot bootstrap eureka server :", e); throw new RuntimeException("Cannot bootstrap eureka server :", e); } }
初始化中的第一個方法initEurekaEnvironment(),在這裡,其實會呼叫ConfigurationManager.getConfigInstance()方法,這個方法,其實就是初始化ConfigurationManager的例項,也就是一個配置管理器的初始化的這麼一個過程。ConfigurationManager是什麼呢?看字面意思都猜的出來,配置管理器,管理eureka自己的所有的配置,讀取配置檔案裡的配置到記憶體裡,供後續的eureka-server執行來使用。
initEurekaEnvironment方法中注意以下三點:
(1)dataCenter預設值:初始化資料中心的配置,如果沒有配置的話,就是DEFAULT
(2)environment預設值:初始化eurueka執行的環境,如果你沒有配置的話,預設就給你設定為test環境
(3)ConfigurationManager.getConfigInstance()返回一個配置管理器例項,用於管理eureka自己的所有的配置,讀取配置檔案裡的配置到記憶體裡,供後續的eureka-server執行來使用。此處採取的是單例模式
protected void initEurekaEnvironment() throws Exception { logger.info("Setting the eureka configuration.."); String dataCenter = ConfigurationManager.getConfigInstance().getString(EUREKA_DATACENTER); if (dataCenter == null) { logger.info("Eureka data center value eureka.datacenter is not set, defaulting to default"); ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, DEFAULT); } else { ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, dataCenter); } String environment = ConfigurationManager.getConfigInstance().getString(EUREKA_ENVIRONMENT); if (environment == null) { ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST); logger.info("Eureka environment value eureka.environment is not set, defaulting to test"); } }
接著點進去getConfigInstance方法,看下人家單例模式的使用,比較廣泛的運用,倒不是說用那個靜態內部類的方法,很多人覺得那樣寫加了一個內部類,比較麻煩。在各種開源專案裡,你看原始碼,人家用的比較多的,其實是double check + volatile。
static volatile AbstractConfiguration instance = null;
//經典單例模式的使用 double check + volatile方式
public static AbstractConfiguration getConfigInstance() {
if (instance == null) {
synchronized (ConfigurationManager.class) {
if (instance == null) {
instance = getConfigInstance(Boolean.getBoolean(DynamicPropertyFactory.DISABLE_DEFAULT_CONFIG));
}
}
}
return instance;
}
接著點進去getConfigInstance方法如下:
private static AbstractConfiguration getConfigInstance(boolean defaultConfigDisabled) {
if (instance == null && !defaultConfigDisabled) {
instance = createDefaultConfigInstance();
registerConfigBean();
}
return instance;
}
(1)接著點進去createDefaultConfigInstance方法,上邊的getConfigInstance主要建立一個ConcurrentCompositeConfiguration例項,這個東西,其實就是代表了所謂的配置,包括了eureka需要的所有的配置。
(2)就是往下面的那個ConcurrentCompositeConfiguration例項加入了一堆別的config,然後搞完了以後,就直接返回了這個例項,就是作為所謂的那個配置的單例
private static AbstractConfiguration createDefaultConfigInstance() {
ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();
try {
DynamicURLConfiguration defaultURLConfig = new DynamicURLConfiguration();
config.addConfiguration(defaultURLConfig, URL_CONFIG_NAME);
} catch (Throwable e) {
logger.warn("Failed to create default dynamic configuration", e);
}
if (!Boolean.getBoolean(DISABLE_DEFAULT_SYS_CONFIG)) {
SystemConfiguration sysConfig = new SystemConfiguration();
config.addConfiguration(sysConfig, SYS_CONFIG_NAME);
}
if (!Boolean.getBoolean(DISABLE_DEFAULT_ENV_CONFIG)) {
EnvironmentConfiguration envConfig = new EnvironmentConfiguration();
config.addConfiguration(envConfig, ENV_CONFIG_NAME);
}
ConcurrentCompositeConfiguration appOverrideConfig = new ConcurrentCompositeConfiguration();
config.addConfiguration(appOverrideConfig, APPLICATION_PROPERTIES);
config.setContainerConfigurationIndex(config.getIndexOfConfiguration(appOverrideConfig));
return config;
}
在初始化這個ConcurrentCompositeConfiguration例項的時候,呼叫了坑爹的clear()方法,fireEvent()釋出了一個事件(EVENT_CLEAR),fireEvent()這個方法其實是父類的方法,牽扯比較複雜的另外一個專案(ConfigurationManager本身不是屬於eureka的原始碼,是屬於netflix config專案的原始碼)。
public ConcurrentCompositeConfiguration()
{
clear();
}
@Override
public final void clear()
{
fireEvent(EVENT_CLEAR, null, null, true);
configList.clear();
namedConfigurations.clear();
// recreate the in memory configuration
containerConfiguration = new ConcurrentMapConfiguration();
containerConfiguration.setThrowExceptionOnMissing(isThrowExceptionOnMissing());
containerConfiguration.setListDelimiter(getListDelimiter());
containerConfiguration.setDelimiterParsingDisabled(isDelimiterParsingDisabled());
containerConfiguration.addConfigurationListener(eventPropagater);
configList.add(containerConfiguration);
overrideProperties = new ConcurrentMapConfiguration();
overrideProperties.setThrowExceptionOnMissing(isThrowExceptionOnMissing());
overrideProperties.setListDelimiter(getListDelimiter());
overrideProperties.setDelimiterParsingDisabled(isDelimiterParsingDisabled());
overrideProperties.addConfigurationListener(eventPropagater);
fireEvent(EVENT_CLEAR, null, null, false);
containerConfigurationChanged = false;
invalidate();
}
到此為止,initEurekaEnvironment的初始化環境的邏輯,就結束了
總結:
(1)ConfigurationManager的單例初始化的過程
(2)重點理解,ConfigurationnManager原始碼中體現的double chehck + volatile的單例實現模式的思想和技巧
(3)理解initEurekaEnvironment,初始化環境的邏輯,資料中心 + 執行環境,沒設定的話,都給你搞成預設的和測試的