eureka的EurekaBootStrap(源碼解讀三)

ServletContextListener

在eureka-server這個模塊下,有一個web.xml,我們知道,它是用來給tomcat加載的。

在這個文件裏,加載了一個listener

 <listener>
    <listener-class>com.netflix.eureka.EurekaBootStrap</listener-class>
  </listener>

那麼,這個EurekaBootStrap是什麼呢?

在eureka-core中找到這個類:

打開分析:

/**
 * The class that kick starts the eureka server.
 */

它啓動了eureka server。

EurekaBootStrap 實現了ServletContextListener

後者是個java擴展類:

Interface for receiving notification events about ServletContext lifecycle changes.

In order to receive these notification events, the implementation class must be either declared in the deployment descriptor of the web application, annotated with WebListener, or registered via one of the addListener methods defined on ServletContext.

Implementations of this interface are invoked at their contextInitialized(javax.servlet.ServletContextEvent) method in the order in which they have been declared, and at their contextDestroyed(javax.servlet.ServletContextEvent) method in reverse order.




它是用來監聽Servlet Context生命週期的變化的。

文檔說,要把ServletContextListener的實現類放在deployment descriptor of the web application,這就是我們web.xml

這個類一加載,就會調用contextInitialized方法完成初始化。

 /**
     * Initializes Eureka, including syncing up with other Eureka peers and publishing the registry.
     *
     * @see
     * javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
     */
    @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);
        }
    }




eureka server啓動要幹那幾件事情呢?

初始化eureka,與其他eureka peers同步,然後發佈註冊表。

核心邏輯在這兩行代碼:

 initEurekaEnvironment();
 initEurekaServerContext();

一個是初始化eureka環境,一個是初始化eureka server的上下文。


initEurekaEnvironment()

  /**
     * Users can override to initialize the environment themselves.
     */
    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");
        }
    }




它要在這裏設置兩個配置:ARCHAIUS_DEPLOYMENT_DATACENTERARCHAIUS_DEPLOYMENT_ENVIRONMENT,部署數據中心和部署環境。

eureka.datacenter默認是default,你也可以將其設置爲cloud,如此eureka便知道自己會部署到AWS上:

Eureka can be run in both AWS and non-AWS environments.
If you are running in the cloud environment, you will need to pass in the java commandline property -Deureka.datacenter=cloud so that the Eureka Client/Server knows to initialize the information specific to AWS cloud.

eureka.environment默認是test,也可以設置成prod

這些都是小事,關鍵是這行代碼:

ConfigurationManager.getConfigInstance()
  /**
     * Get the current system wide configuration. If there has not been set, it will return a default
     * {@link ConcurrentCompositeConfiguration} which contains a SystemConfiguration from Apache Commons
     * Configuration and a {@link DynamicURLConfiguration}.
     */
    public static AbstractConfiguration getConfigInstance() {
        if (instance == null) {
            synchronized (ConfigurationManager.class) {
                if (instance == null) {
                    instance = getConfigInstance(Boolean.getBoolean(DynamicPropertyFactory.DISABLE_DEFAULT_CONFIG));
                }
            }
        }
        return instance;
    }



private static AbstractConfiguration getConfigInstance(boolean defaultConfigDisabled) {
        if (instance == null && !defaultConfigDisabled) {
            instance = createDefaultConfigInstance();
            registerConfigBean();
        }
        return instance;        
    }
 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;
    }




這裏還用到了經典的double-check單例模式。

我們最終想知道的是:默認的配置是如何生成的?

首先,我們瞅一下所有configuration的關係:

所以說,archaius是對apache Configuration的擴展。

這裏它將所有配置加到一個組合配置ConcurrentCompositeConfiguration中,其中:

  • URL_CONFIG_NAME:通過url拉取的配置
  • SYS_CONFIG_NAME: 系統屬性配置,通過System.getProperties()獲取的配置
  • ENV_CONFIG_NAME: 環境配置,通過System.getenv()獲取的配置
  • containerConfiguration: 容器配置,ConfigurationManager.getConfigInstance().setProperty(key,value)設置的配置
  • APPLICATION_PROPERTIES: 調用configurationManager.loadAppOverrideProperties設置的配置

最終返回出去的是一個AbstractConfiguration對象。

AbstractConfiguration對象受到ConfigurationManager管理。

ConfigurationManager是eureka的配置管理中心。

一句話,initEurekaEnvironment()的作用是加載所有的配置。


initEurekaServerContext()

 /**
     * init hook for server context. Override for custom logic.
     */
    protected void initEurekaServerContext() throws Exception {
        EurekaServerConfig eurekaServerConfig = new DefaultEurekaServerConfig();

        // For backward compatibility
        JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH);
        XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH);

        logger.info("Initializing the eureka client...");
        logger.info(eurekaServerConfig.getJsonCodecName());
        ServerCodecs serverCodecs = new DefaultServerCodecs(eurekaServerConfig);

        ApplicationInfoManager applicationInfoManager = null;

        if (eurekaClient == null) {
            EurekaInstanceConfig instanceConfig = isCloud(ConfigurationManager.getDeploymentContext())
                    ? new CloudInstanceConfig()
                    : new MyDataCenterInstanceConfig();
            
            applicationInfoManager = new ApplicationInfoManager(
                    instanceConfig, new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get());
            
            EurekaClientConfig eurekaClientConfig = new DefaultEurekaClientConfig();
            eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig);
        } else {
            applicationInfoManager = eurekaClient.getApplicationInfoManager();
        }

        PeerAwareInstanceRegistry registry;
        if (isAws(applicationInfoManager.getInfo())) {
            registry = new AwsInstanceRegistry(
                    eurekaServerConfig,
                    eurekaClient.getEurekaClientConfig(),
                    serverCodecs,
                    eurekaClient
            );
            awsBinder = new AwsBinderDelegate(eurekaServerConfig, eurekaClient.getEurekaClientConfig(), registry, applicationInfoManager);
            awsBinder.start();
        } else {
            registry = new PeerAwareInstanceRegistryImpl(
                    eurekaServerConfig,
                    eurekaClient.getEurekaClientConfig(),
                    serverCodecs,
                    eurekaClient
            );
        }

        PeerEurekaNodes peerEurekaNodes = getPeerEurekaNodes(
                registry,
                eurekaServerConfig,
                eurekaClient.getEurekaClientConfig(),
                serverCodecs,
                applicationInfoManager
        );

        serverContext = new DefaultEurekaServerContext(
                eurekaServerConfig,
                serverCodecs,
                registry,
                peerEurekaNodes,
                applicationInfoManager
        );

        EurekaServerContextHolder.initialize(serverContext);

        serverContext.initialize();
        logger.info("Initialized server context");

        // Copy registry from neighboring eureka node
        int registryCount = registry.syncUp();
        registry.openForTraffic(applicationInfoManager, registryCount);

        // Register all monitoring statistics.
        EurekaMonitors.registerAllStats();
    }



這個方法的信息量巨大,這篇文章我們只講第一行代碼:

 EurekaServerConfig eurekaServerConfig = new DefaultEurekaServerConfig();

這個類是用來管理eureka的配置的。

它的構造器裏調用了init()方法:

  private void init() {
        String env = ConfigurationManager.getConfigInstance().getString(
                EUREKA_ENVIRONMENT, TEST);
        ConfigurationManager.getConfigInstance().setProperty(
                ARCHAIUS_DEPLOYMENT_ENVIRONMENT, env);

        String eurekaPropsFile = EUREKA_PROPS_FILE.get();
        try {
            // ConfigurationManager
            // .loadPropertiesFromResources(eurekaPropsFile);
            ConfigurationManager
                    .loadCascadedPropertiesFromResources(eurekaPropsFile);
        } catch (IOException e) {
            logger.warn(
                    "Cannot find the properties specified : {}. This may be okay if there are other environment "
                            + "specific properties or the configuration is installed with a different mechanism.",
                    eurekaPropsFile);
        }
    }

這個方法用來加載server的配置文件。

爲了瞭解細節,我們新建一個eureka-server.properties

並在裏面寫一個屬性:

eureka.peerEurekaNodesUpdateIntervalMs=200

並在DefaultEurekaServerConfig中寫一個main方法:

debug一下。

進到loadCascadePropertiesFromResources中:

然後到getPropertiesFromFile

一路跟下去,最後它是用Properties來讀的:

解析properties文件過後,將裏面的值放進ConcurrentMapConfigurationConcurrentHashMap中

然後通過

 ((AggregatedConfiguration) instance).addConfiguration(config, configName);

將其加入一個configList中:

如此eureka server的配置就加載好了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章