1. 程式人生 > >Netflix Eureka原始碼分析(3)——listener(EurekaBootStrap監聽類)分析

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

data center

(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,初始化環境的邏輯,資料中心 + 執行環境,沒設定的話,都給你搞成預設的和測試的