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的配置就加载好了。

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