Netflix Eureka - 服务端环境初始化

121 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

eureka-server启动

eureka-server作为web应用启动,执行监听器(com.netflix.eureka.EurekaBootStrap)初始化方法(contextInitialized(ServletContextEvent sce))。

  • eureka-server 初始化环境:initEurekaEnvironment();
  • eureka-server 初始化上下文:initEurekaServerContext();
package com.netflix.eureka;

/**
 * The class that kick starts the eureka server.
 *
 * <p>
 * The eureka server is configured by using the configuration
 * {@link EurekaServerConfig} specified by <em>eureka.server.props</em> in the
 * classpath.  The eureka client component is also initialized by using the
 * configuration {@link EurekaInstanceConfig} specified by
 * <em>eureka.client.props</em>. If the server runs in the AWS cloud, the eureka
 * server binds it to the elastic ip as specified.
 * </p>
 *
 * @author Karthik Ranganathan, Greg Kim, David Liu
 */

/**
 * 本质是个监听器
 * <p>
 * contextInitialized(ServletContextEvent sce) :当Servlet 容器启动Web 应用时调用该方法。在调用完该方法之后,容器再对Filter
 * 初始化,并且对那些在Web 应用启动时就需要被初始化的Servlet 进行初始化。
 * <p>
 * contextDestroyed(ServletContextEvent sce) :当Servlet 容器终止Web 应用时调用该方法。在调用该方法之前,容器会先销毁所有的Servlet
 * 和Filter 过滤器。
 */
public class EurekaBootStrap implements ServletContextListener {

    /**
     * Initializes Eureka, including syncing up with other Eureka peers and publishing the registry.
     *
     * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
     */
    /**
     * 当Servlet 容器启动Web 应用时调用该方法。
     * 在调用完该方法之后,容器再对Filter初始化,并且对那些在Web 应用启动时就需要被初始化的Servlet 进行初始化。
     *
     * @param event
     */
    @Override
    public void contextInitialized(ServletContextEvent event) {
        try {
            // 初始化 eureka-server 环境
            initEurekaEnvironment();
            // 初始化 eureka-server 容器
            initEurekaServerContext();

            // 获取 servletContext
            ServletContext sc = event.getServletContext();

            // 设置 servletContext 属性
            // com.netflix.eureka.EurekaServerContext = EurekaServerContext 实例
            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初始化环境:initEurekaEnvironment();

配置管理器(double-checked locking

ConfigurationManager.instance =(AbstractConfiguration)ConcurrentCompositeConfiguration;     
ConcurrentCompositeConfiguration.addConfiguration(AbstractConfiguration);

创建一个 (AbstractConfiguration)ConcurrentCompositeConfiguration 实例,加入其他(AbstractConfiguration)config

ConfigurationManager.getConfigInstance()
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 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;
}

初始化数据中心

初始化数据中心(eureka.datacenter)的配置,如果没有配置的话,就是DEFAULT(default)

初始化运行环境

初始化eureka运行的环境(eureka.environment),如果你没有配置的话,默认设置为test环境

eureka-server 初始化上下文:initEurekaServerContext();

/**
 * init hook for server context. Override for custom logic.
 */
protected void initEurekaServerContext() throws Exception {
    // 获取 eureka-server 配置
    // 面向接口的配置项读取
    // DefaultEurekaServerConfig 创建实例时,执行init()方法,完成eureka-server.properties配置项的加载
    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;

    // 获取应用管理器及Eureka客户端
    if (eurekaClient == null) {
        // EurekaClient 如果为 null
        // 获取 eureka实例配置 实例
        EurekaInstanceConfig instanceConfig =
                isCloud(ConfigurationManager.getDeploymentContext()) ?
                        new CloudInstanceConfig() : new MyDataCenterInstanceConfig();

        // 获取 应用信息管理器
        applicationInfoManager = new ApplicationInfoManager(
                // eureka实例配置 实例
                instanceConfig,
                // 基于 eureka实例配置 实例 获取 InstanceInfo 实例信息 实例
                new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get());

        // 获取 eureka客户端配置 实例
        EurekaClientConfig eurekaClientConfig = new DefaultEurekaClientConfig();
        // 获取 (EurekaClient)DiscoveryClient eureka客户端 实例
        eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig);
    } else {
        // EurekaClient 不为空
        // 获取 应用信息管理器
        applicationInfoManager = eurekaClient.getApplicationInfoManager();
    }

    PeerAwareInstanceRegistry registry;
    if (isAws(applicationInfoManager.getInfo())) { // 判断 实例信息 是不是 aws类型实例
        // 如果是 aws类型实例
        registry = new AwsInstanceRegistry(eurekaServerConfig,
                eurekaClient.getEurekaClientConfig(), serverCodecs, eurekaClient);
        awsBinder = new AwsBinderDelegate(eurekaServerConfig,
                eurekaClient.getEurekaClientConfig(), registry, applicationInfoManager);
        awsBinder.start();
    } else {
        // 如果不是 aws类型实例
        // 获取 PeerAwareInstanceRegistry 注册表
        // 服务配置信息、客户端配置信息、服务编码组件、客户端组件
        registry = new PeerAwareInstanceRegistryImpl(eurekaServerConfig,
                eurekaClient.getEurekaClientConfig(), serverCodecs, eurekaClient);
    }

    // 根据 注册表、服务端配置信息、客户端配置信息、服务编码组件、应用信息管理器等获取 Eureka单个服务节点 实例
    PeerEurekaNodes peerEurekaNodes = getPeerEurekaNodes(registry, eurekaServerConfig,
            eurekaClient.getEurekaClientConfig(), serverCodecs, applicationInfoManager);

    // 获取 eureka服务上下文
    serverContext = new DefaultEurekaServerContext(eurekaServerConfig, serverCodecs, registry
            , peerEurekaNodes, applicationInfoManager);

    // 构建 EurekaServerContextHolder 持有 EurekaServerContext serverContext
    EurekaServerContextHolder.initialize(serverContext);

    // EurekaServerContext serverContext eureka服务上下文初始化
    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();
}

配置文件加载

面向接口的配置项读取

/**
 * Configuration information required by the eureka server to operate.
 *
 * <p>
 * Most of the required information is provided by the default configuration
 * {@link com.netflix.eureka.DefaultEurekaServerConfig}.
 *
 * Note that all configurations are not effective at runtime unless and
 * otherwise specified.
 * </p>
 *
 * @author Karthik Ranganathan
 *
 */
public interface EurekaServerConfig {

}

EurekaServerConfig 接口定义了所有eureka server需要的所有配置项,通过getXXX()的方法获取。

EurekaServerConfig 接口代表了eureka-server需要的所有的配置项。

/**
 * A default implementation of eureka server configuration as required by
 * {@link EurekaServerConfig}.
 *
 * <p>
 * The information required for configuring eureka server is provided in a
 * configuration file.The configuration file is searched for in the classpath
 * with the name specified by the property <em>eureka.server.props</em> and with
 * the suffix <em>.properties</em>. If the property is not specified,
 * <em>eureka-server.properties</em> is assumed as the default.The properties
 * that are looked up uses the <em>namespace</em> passed on to this class.
 * </p>
 *
 * <p>
 * If the <em>eureka.environment</em> property is specified, additionally
 * <em>eureka-server-<eureka.environment>.properties</em> is loaded in addition
 * to <em>eureka-server.properties</em>.
 * </p>
 *
 * @author Karthik Ranganathan
 */
@Singleton
public class DefaultEurekaServerConfig implements EurekaServerConfig {
}

DefaultEurekaServerConfig 类实现 EurekaServerConfig 接口。

执行构造方法时,会执行一个init()方法,完成eureka-server.properties文件和eureka-server-环境.properties中的配置项的加载。

private static final DynamicStringProperty EUREKA_PROPS_FILE = DynamicPropertyFactory.getInstance().getStringProperty("eureka.server.props", "eureka"+ "-server");

EUREKA_PROPS_FILE,对应着要加载的eureka的配置文件的名字。默认是eureka-server

/**
 * 加载properties中的配置,放入ConfigurationManager,由ConfigurationManager管理
 */
private void init() {
    // 获取环境,默认是test(为了拼接配置文件名:eureka-server-test.properties)
    String env = ConfigurationManager.getConfigInstance().getString(EUREKA_ENVIRONMENT, TEST);
    // 更新 archaius.deployment.environment 对应的是什么环境
    ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, env);

    // 要加载的eureka的配置文件的名字。默认是 eureka-server
    String eurekaPropsFile = EUREKA_PROPS_FILE.get();
    try {
        // 加载eureka-server.properties和eureka-server-环境.properties中的配置,加载到了Properties对象中去;
        // eureka-server-环境.properties 覆盖 相同key的 eureka-server-环境.properties 配置
        // 将加载出来的Properties中的配置项都放到ConfigurationManager中去,由ConfigurationManager管理
        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);
    }
}

加载eureka-server.properties

  1. 创建DefaultEurekaServerConfig对象

  2. 执行DefaultEurekaServerConfig构造方法时调用init()方法

  3. 加载eureka-server.properties和eureka-server.properties中的配置到Properties对象中,将Properties对象中的配置放到ConfigurationManager中去

  4. DefaultEurekaServerConfig 提供的获取配置项的各个方法,都是通过硬编码的配置项名称,从DynamicPropertyFactory中获取配置项的值。

  5. 在获取配置项的时候,如果没有配置,那么就会有默认的值,全部属性都是有默认值的

获取DefaultEurekaServerConfig中的配置项的值

@Override
public String getAWSAccessId() {
    String aWSAccessId =
            configInstance.getStringProperty(namespace + "awsAccessId", null).get();

    if (null != aWSAccessId) {
        return aWSAccessId.trim();
    } else {
        return null;
    }

}

DynamicPropertyFactory与ConfigurationManager

DynamicPropertyFactory在获取实例时,会判断config是否为null,如果为null的话,会从ConfigurationManager获取config。
DynamicPropertyFactory是从ConfigurationManager那儿来的,所以也包含了所有配置项的值
private static final DynamicPropertyFactory configInstance = com.netflix.config.DynamicPropertyFactory.getInstance();
public static DynamicPropertyFactory getInstance() {
    if (config == null) {
        synchronized (ConfigurationManager.class) {
            if (config == null) {
                AbstractConfiguration configFromManager = ConfigurationManager.getConfigInstance();
                if (configFromManager != null) {
                    initWithConfigurationSource(configFromManager);
                    initializedWithDefaultConfig = !ConfigurationManager.isConfigurationInstalled();
                    logger.info("DynamicPropertyFactory is initialized with configuration sources: " + configFromManager);
                }
            }
        }
    }
    return instance;
}
加载properties文件到Properties对象,放到ConfigurationManager中
从DynamicPropertyFactory读取
存:configurationManager
取:DynamicPropertyFactory