前言

上篇文章已经介绍了 为何要读netflix eureka源码了,这里就不再概述,下面开始正式源码解读的内容。

如若转载 请标明来源:一枝花算不算浪漫

代码总览

还记得上文中,我们通过web.xml找到了eureka server入口的类EurekaBootStrap,这里我们就先来简单地看下:

  1. /**
  2. * The class that kick starts the eureka server. 负责启动Eureka server的类
  3. *
  4. * <p>
  5. * 这里要注意两个关键点:
  6. * eureka server对应的配置类为:EurekaServerConfig
  7. * eureka client对应的配置类为:EurekaInstanceConfig
  8. *
  9. * The eureka server is configured by using the configuration
  10. * {@link EurekaServerConfig} specified by <em>eureka.server.props</em> in the
  11. * classpath. The eureka client component is also initialized by using the
  12. * configuration {@link EurekaInstanceConfig} specified by
  13. * <em>eureka.client.props</em>. If the server runs in the AWS cloud, the eureka
  14. * server binds it to the elastic ip as specified.
  15. * </p>
  16. *
  17. * @author Karthik Ranganathan, Greg Kim, David Liu
  18. *
  19. * 负责EurekaServer初始化的类
  20. */
  21. public class EurekaBootStrap implements ServletContextListener {
  22. /**
  23. * Initializes Eureka, including syncing up with other Eureka peers and publishing the registry.
  24. *
  25. * @see
  26. * javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
  27. */
  28. @Override
  29. public void contextInitialized(ServletContextEvent event) {
  30. try {
  31. initEurekaEnvironment();
  32. initEurekaServerContext();
  33. ServletContext sc = event.getServletContext();
  34. sc.setAttribute(EurekaServerContext.class.getName(), serverContext);
  35. } catch (Throwable e) {
  36. logger.error("Cannot bootstrap eureka server :", e);
  37. throw new RuntimeException("Cannot bootstrap eureka server :", e);
  38. }
  39. }
  40. }

看下注释 我们可以了解到几个关键点:

  1. eureka server对应的配置类为:EurekaServerConfig
  2. eureka client对应的配置类为:EurekaInstanceConfig
  3. EurekaBootStrap implements ServletContextListener, 所以这里会直接执行contextInitialized方法。

Eureka-Server 环境配置

初始化enviroment

接着近一步往下跟,这里可以先看 initEurekaEnvironment()

代码如下:

  1. protected void initEurekaEnvironment() throws Exception {
  2. logger.info("Setting the eureka configuration..");
  3. // 获取dataCenter数据中心 这里重点看ConfigurationManager
  4. // ConfigurationManager:配置管理器,管理eureka自己所有的配置,
  5. // 重点:getConfigInstance里面使用的是volatile+synchronized+double check模式的单例模式
  6. /**
  7. * ConfigurationManager 创建过程:(继续往后跟读代码)
  8. * 1、创建一个ConcurrentCompositeConfiguration实例,这个类代表了所谓的配置,包括eureka需要的所有配置。
  9. * 2、往ConcurrentCompositeConfiguration加入一堆config,然后返回ConfigurationManager实例
  10. * 3、初始化数据中心的配置,如果没有配置的话就是default data center
  11. * 4、初始化eureka 运行的环境,如果没有配置的话,默认就是test环境
  12. */
  13. String dataCenter = ConfigurationManager.getConfigInstance().getString(EUREKA_DATACENTER);
  14. // 初始化数据中心,没有配置的话 使用DEFAULT data center
  15. if (dataCenter == null) {
  16. logger.info("Eureka data center value eureka.datacenter is not set, defaulting to default");
  17. ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, DEFAULT);
  18. } else {
  19. ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, dataCenter);
  20. }
  21. // 获取eureka server运行环境,没有配置的话默认使用test环境
  22. // 后面读取配置文件会根据运行环境读取,比如eureka-server-test.properties
  23. String environment = ConfigurationManager.getConfigInstance().getString(EUREKA_ENVIRONMENT);
  24. if (environment == null) {
  25. ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST);
  26. logger.info("Eureka environment value eureka.environment is not set, defaulting to test");
  27. }
  28. }

这里注释写的比较详细,且这里有两个重点:

  1. getConfigInstance里面使用的是volatile+synchronized+double check模式的单例模式
  2. ConfigurationManager 创建过程

getConfigInstance

这里一个关键点 是使用了很经典的double check 单例模式。

这种单例是一种线程安全的方式,里面使用了volatile+synchronized+double check,具体秒在何处 我这里就不展开讲解了,搜索double check单例模式就会有很多解析文章,这里直接看代码。

  1. static volatile AbstractConfiguration instance = null;
  2. /**
  3. * Get the current system wide configuration. If there has not been set, it will return a default
  4. * {@link ConcurrentCompositeConfiguration} which contains a SystemConfiguration from Apache Commons
  5. * Configuration and a {@link DynamicURLConfiguration}.
  6. */
  7. public static AbstractConfiguration getConfigInstance() {
  8. if (instance == null) {
  9. synchronized (ConfigurationManager.class) {
  10. if (instance == null) {
  11. instance = getConfigInstance(Boolean.getBoolean(DynamicPropertyFactory.DISABLE_DEFAULT_CONFIG));
  12. }
  13. }
  14. }
  15. return instance;
  16. }

这里instance用volatile修饰来保证线程之间的可见性和有序性(禁止指令重排序),一般的对象创建过程都是非原子性的,内部会发生指令重排序的情况,所以加上volatile可以防止指令重排。用synchronized来保证线程串行化,double check来保证不被单例化。

接着我们就继续往下跟,看看ConfigurationManager的创建过程。

ConfigurationManager 创建

  1. private static AbstractConfiguration getConfigInstance(boolean defaultConfigDisabled) {
  2. if (instance == null && !defaultConfigDisabled) {
  3. instance = createDefaultConfigInstance();
  4. registerConfigBean();
  5. }
  6. return instance;
  7. }
  8. private static AbstractConfiguration createDefaultConfigInstance() {
  9. ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();
  10. try {
  11. DynamicURLConfiguration defaultURLConfig = new DynamicURLConfiguration();
  12. config.addConfiguration(defaultURLConfig, URL_CONFIG_NAME);
  13. } catch (Throwable e) {
  14. logger.warn("Failed to create default dynamic configuration", e);
  15. }
  16. if (!Boolean.getBoolean(DISABLE_DEFAULT_SYS_CONFIG)) {
  17. SystemConfiguration sysConfig = new SystemConfiguration();
  18. config.addConfiguration(sysConfig, SYS_CONFIG_NAME);
  19. }
  20. if (!Boolean.getBoolean(DISABLE_DEFAULT_ENV_CONFIG)) {
  21. EnvironmentConfiguration envConfig = new EnvironmentConfiguration();
  22. config.addConfiguration(envConfig, ENV_CONFIG_NAME);
  23. }
  24. ConcurrentCompositeConfiguration appOverrideConfig = new ConcurrentCompositeConfiguration();
  25. config.addConfiguration(appOverrideConfig, APPLICATION_PROPERTIES);
  26. config.setContainerConfigurationIndex(config.getIndexOfConfiguration(appOverrideConfig));
  27. return config;
  28. }
  29. public ConcurrentCompositeConfiguration()
  30. {
  31. clear();
  32. }
  33. public final void clear()
  34. {
  35. fireEvent(EVENT_CLEAR, null, null, true);
  36. configList.clear();
  37. namedConfigurations.clear();
  38. // recreate the in memory configuration
  39. containerConfiguration = new ConcurrentMapConfiguration();
  40. containerConfiguration.setThrowExceptionOnMissing(isThrowExceptionOnMissing());
  41. containerConfiguration.setListDelimiter(getListDelimiter());
  42. containerConfiguration.setDelimiterParsingDisabled(isDelimiterParsingDisabled());
  43. containerConfiguration.addConfigurationListener(eventPropagater);
  44. configList.add(containerConfiguration);
  45. overrideProperties = new ConcurrentMapConfiguration();
  46. overrideProperties.setThrowExceptionOnMissing(isThrowExceptionOnMissing());
  47. overrideProperties.setListDelimiter(getListDelimiter());
  48. overrideProperties.setDelimiterParsingDisabled(isDelimiterParsingDisabled());
  49. overrideProperties.addConfigurationListener(eventPropagater);
  50. fireEvent(EVENT_CLEAR, null, null, false);
  51. containerConfigurationChanged = false;
  52. invalidate();
  53. }

上面代码是比较多,如果一行行去抠细节 真的就没有必要了,这里我们只是看一些重点的流程,我们上面注释也写到过ConfigurationManager的创建过程:

1、创建一个ConcurrentCompositeConfiguration实例,这个类代表了所谓的配置,包括eureka需要的所有配置。

2、往ConcurrentCompositeConfiguration加入一堆config,然后返回ConfigurationManager实例

这里我是不建议太过于扣细节的,因为往往这些细枝末节的东西会将我们绕进去。

关于ConfigurationManager具体的细节这里也有两篇比较好的文章推荐:

  1. 关于 Eureka 启动的说明
  2. 微服务动态配置组件netflix archaius

Eureka-Server 上下文加载

先看代码:

  1. protected void initEurekaServerContext() throws Exception {
  2. // 1、加载eureka-server properties文件中和配置
  3. EurekaServerConfig eurekaServerConfig = new DefaultEurekaServerConfig();
  4. // For backward compatibility
  5. JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH);
  6. XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH);
  7. logger.info("Initializing the eureka client...");
  8. logger.info(eurekaServerConfig.getJsonCodecName());
  9. ServerCodecs serverCodecs = new DefaultServerCodecs(eurekaServerConfig);
  10. // 2、初始化一个ApplicationInfoManager,和第3步创建eureka client相关,后续会讲解
  11. ApplicationInfoManager applicationInfoManager = null;
  12. // 3、初始化eureka-server内部的一个eureka-client(用来跟其他的eureka-server节点做注册和通信)
  13. // 类的开头已经说明了:EurekaInstanceConfig其实就是eureka client相关的配置类
  14. if (eurekaClient == null) {
  15. EurekaInstanceConfig instanceConfig = isCloud(ConfigurationManager.getDeploymentContext())
  16. ? new CloudInstanceConfig()
  17. : new MyDataCenterInstanceConfig();
  18. applicationInfoManager = new ApplicationInfoManager(
  19. instanceConfig, new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get());
  20. EurekaClientConfig eurekaClientConfig = new DefaultEurekaClientConfig();
  21. eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig);
  22. } else {
  23. applicationInfoManager = eurekaClient.getApplicationInfoManager();
  24. }
  25. // 3、处理注册相关的事情
  26. PeerAwareInstanceRegistry registry;
  27. if (isAws(applicationInfoManager.getInfo())) {
  28. registry = new AwsInstanceRegistry(
  29. eurekaServerConfig,
  30. eurekaClient.getEurekaClientConfig(),
  31. serverCodecs,
  32. eurekaClient
  33. );
  34. awsBinder = new AwsBinderDelegate(eurekaServerConfig, eurekaClient.getEurekaClientConfig(), registry, applicationInfoManager);
  35. awsBinder.start();
  36. } else {
  37. registry = new PeerAwareInstanceRegistryImpl(
  38. eurekaServerConfig,
  39. eurekaClient.getEurekaClientConfig(),
  40. serverCodecs,
  41. eurekaClient
  42. );
  43. }
  44. // 4、处理peer节点相关的事情
  45. PeerEurekaNodes peerEurekaNodes = getPeerEurekaNodes(
  46. registry,
  47. eurekaServerConfig,
  48. eurekaClient.getEurekaClientConfig(),
  49. serverCodecs,
  50. applicationInfoManager
  51. );
  52. // 5、完成eureka-server上下文(context)的构建及初始化
  53. serverContext = new DefaultEurekaServerContext(
  54. eurekaServerConfig,
  55. serverCodecs,
  56. registry,
  57. peerEurekaNodes,
  58. applicationInfoManager
  59. );
  60. EurekaServerContextHolder.initialize(serverContext);
  61. serverContext.initialize();
  62. logger.info("Initialized server context");
  63. // Copy registry from neighboring eureka node
  64. // 6、处理一些善后的事情,从相邻的eureka节点拷贝注册信息
  65. int registryCount = registry.syncUp();
  66. registry.openForTraffic(applicationInfoManager, registryCount);
  67. // Register all monitoring statistics.
  68. // 7、注册所有的监控统计项
  69. EurekaMonitors.registerAllStats();
  70. }

代码有点长,加载context信息分为了上面注释的好几步,代码注释都有写

加载eureka-server properties文件中和配置

EurekaServerConfig eurekaServerConfig = new DefaultEurekaServerConfig();

  1. private static final DynamicStringProperty EUREKA_PROPS_FILE = DynamicPropertyFactory.getInstance().getStringProperty("eureka.server.props","eureka-server");
  2. public DefaultEurekaServerConfig() {
  3. init();
  4. }
  5. private void init() {
  6. String env = ConfigurationManager.getConfigInstance().getString(
  7. EUREKA_ENVIRONMENT, TEST);
  8. ConfigurationManager.getConfigInstance().setProperty(
  9. ARCHAIUS_DEPLOYMENT_ENVIRONMENT, env);
  10. String eurekaPropsFile = EUREKA_PROPS_FILE.get();
  11. try {
  12. // ConfigurationManager
  13. // .loadPropertiesFromResources(eurekaPropsFile);
  14. ConfigurationManager
  15. .loadCascadedPropertiesFromResources(eurekaPropsFile);
  16. } catch (IOException e) {
  17. logger.warn(
  18. "Cannot find the properties specified : {}. This may be okay if there are other environment "
  19. + "specific properties or the configuration is installed with a different mechanism.",
  20. eurekaPropsFile);
  21. }
  22. }
  23. public static void loadCascadedPropertiesFromResources(String configName) throws IOException {
  24. Properties props = loadCascadedProperties(configName);
  25. if (instance instanceof AggregatedConfiguration) {
  26. ConcurrentMapConfiguration config = new ConcurrentMapConfiguration();
  27. config.loadProperties(props);
  28. ((AggregatedConfiguration) instance).addConfiguration(config, configName);
  29. } else {
  30. ConfigurationUtils.loadProperties(props, instance);
  31. }
  32. }

首先我们看下EurekaServerConfig

里面包含好多getxxx方法,看一下具体实现:

其中configInstance是DynamicPropertyFactory对象。EurekaServerConfig,这是个接口,这里面有一堆getXXX()的方法,包含了eureka server需要使用的所有的配置,都可以通过这个接口来获取。

想象一下,eureka-sever.properties文件里,都是一个一个的key=value的很多的配置项,肯定是将这些key-value格式的配置项加载到内存的Properties对象去存放,Map。一般来说,如果让我们自己来设计这个读取properties文件的配置的代码,也许我们就是做到将配置加载到Properties对象中就结束了。

EurekaServerConfig,代表了eureka-server需要的所有的配置项,通过接口定义了大量的方法,让你可以从这里获取所有你需要的配置

DefaultEurekaServerConfig就是上面EurekaServerConfig的实现类,创建实例的时候,会执行一个init()方法,在这个方法中,就会完成eureka-server.properties文件中的配置项的加载。EUREKA_PROPS_FILE,对应着要加载的eureka的配置文件的名字。

将加载出来的Properties中的配置项都放到ConfigurationManager中去,由这个ConfigurationManager来管理

比如说eureka-server那个工程里,就有一个src/main/resources/eureka-server.properties文件,只不过里面是空的,全部都用了默认的配置

DefaultEurekaServerConfig.init()方法中,会将eureka-server.properties文件中的配置加载出来,都放到ConfdigurationManager中去,然后在DefaultEurekaServerConfig的各种获取配置项的方法中,配置项的名字是在各个方硬编码的,是从一个DynamicPropertyFactory里面去获取的,你可以认为DynamicPropertyFactory是从ConfigurationManager那儿来的,因为ConfigurationManager中都包含了加载出来的配置了,所以DynamicPropertyFactory里,也可以获取到所有的配置项

在从DynamicPropertyFactory中获取配置项的时候,如果你没配置,那么就用默认值,全部都给你弄好了各个配置项的默认值,相当于所有的配置项的默认值,在DefaultEurekaServerConfig的各个方法中,都可以看到,如果你没配置,那么就用这里的默认值就可以了

加载eureka-server.properties的过程:

(1)创建了一个DefaultEurekaServerConfig对象

(2)创建DefaultEurekaServerConfig对象的时候,在里面会有一个init方法

(3)先是将eureka-server.properties中的配置加载到了一个Properties对象中,然后将Properties对象中的配置放到ConfigurationManager中去,此时ConfigurationManager中去就有了所有的配置了

(4)然后DefaultEurekaServerConfig提供的获取配置项的各个方法,都是通过硬编码的配置项名称,从DynamicPropertyFactory中获取配置项的值,DynamicPropertyFactory是从ConfigurationManager那儿来的,所以也包含了所有配置项的值

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

申明

本文章首发自本人博客:https://www.cnblogs.com/wang-meng 和公众号:壹枝花算不算浪漫,如若转载请标明来源!

感兴趣的小伙伴可关注个人公众号:壹枝花算不算浪漫

【一起学源码-微服务】Nexflix Eureka 源码二:EurekaServer启动之配置文件加载以及面向接口的配置项读取的更多相关文章

  1. 【一起学源码-微服务】Eureka+Ribbon+Feign阶段性总结

    前言 想说的话 这里已经梳理完Eureka.Ribbon.Feign三大组件的基本原理了,今天做一个总结,里面会有一个比较详细的调用关系流程图. 说明 原创不易,如若转载 请标明来源! 博客地址:一枝 ...

  2. 【一起学源码-微服务】Ribbon源码五:Ribbon源码解读汇总篇~

    前言 想说的话 [一起学源码-微服务-Ribbon]专栏到这里就已经全部结束了,共更新四篇文章. Ribbon比较小巧,这里是直接 读的spring cloud 内嵌封装的版本,里面的各种config ...

  3. 【一起学源码-微服务】Ribbon 源码三:Ribbon与Eureka整合原理分析

    前言 前情回顾 上一篇讲了Ribbon的初始化过程,从LoadBalancerAutoConfiguration 到RibbonAutoConfiguration 再到RibbonClientConf ...

  4. 【一起学源码-微服务】Feign 源码一:源码初探,通过Demo Debug Feign源码

    前言 前情回顾 上一讲深入的讲解了Ribbon的初始化过程及Ribbon与Eureka的整合代码,与Eureka整合的类就是DiscoveryEnableNIWSServerList,同时在Dynam ...

  5. 【一起学源码-微服务】Feign 源码二:Feign动态代理构造过程

    前言 前情回顾 上一讲主要看了@EnableFeignClients中的registerBeanDefinitions()方法,这里面主要是 将EnableFeignClients注解对应的配置属性注 ...

  6. 【一起学源码-微服务】Ribbon 源码一:Ribbon概念理解及Demo调试

    前言 前情回顾 前面文章已经梳理清楚了Eureka相关的概念及源码,接下来开始研究下Ribbon的实现原理. 我们都知道Ribbon在spring cloud中担当负载均衡的角色, 当两个Eureka ...

  7. 【一起学源码-微服务】Ribbon 源码二:通过Debug找出Ribbon初始化流程及ILoadBalancer原理分析

    前言 前情回顾 上一讲讲了Ribbon的基础知识,通过一个简单的demo看了下Ribbon的负载均衡,我们在RestTemplate上加了@LoadBalanced注解后,就能够自动的负载均衡了. 本 ...

  8. 【一起学源码-微服务】Ribbon 源码四:进一步探究Ribbon的IRule和IPing

    前言 前情回顾 上一讲深入的讲解了Ribbon的初始化过程及Ribbon与Eureka的整合代码,与Eureka整合的类就是DiscoveryEnableNIWSServerList,同时在Dynam ...

  9. 【一起学源码-微服务】Feign 源码三:Feign结合Ribbon实现负载均衡的原理分析

    前言 前情回顾 上一讲我们已经知道了Feign的工作原理其实是在项目启动的时候,通过JDK动态代理为每个FeignClinent生成一个动态代理. 动态代理的数据结构是:ReflectiveFeign ...

随机推荐

  1. 高性能MySQL3_笔记1_Mysql的架构与历史

    第一层:连接处理.授权认证.安全 第二层:mysql的核心功能,包括查询解析.分析.优化.缓存以及所有的内置函数(例如日期.加密.数学函数), 所有跨存储引擎的功能都在这一层实现:存储过程.触发器.视 ...

  2. 区间问题 codeforces 422c+hiho区间求差问

    先给出一个经典的区间处理方法 对每个区间 我们对其起点用绿色标识  终点用蓝色标识 然后把所有的点离散在一个坐标轴上 如下图 这样做有什么意义呢.由于我们的区间可以离散的放在一条轴上面那么我们在枚举区 ...

  3. Innodb、MYISAM的文件存储结构

    MySQL的每个数据库都对应存放在一个与数据库同名的文件夹中,MySQL数据库文件包括MySQLserver所创建的数据库文件和MySQL所用存储引擎创建的数据库文件. 查看MySql数据库物理文件存 ...

  4. java 常用日期工具类的操作

    获取指定日期的时间戳 /* * 获取指定日期的时间戳 * */ public static long getMillis(String date_str){ try { SimpleDateForma ...

  5. element-ui Cascader 级联选择器 点击label选中

    通过修改label的样式解决: 注意:el-cascader-panel 是直接挂载在body上的,所以需要全局设置 .el-cascader-panel .el-radio{ width: 100% ...

  6. go语言入门(1)

    1,Go语言的核心开发团队-三个大牛 Ken Thompson(肯·汤普森):1983年图灵奖(Turing Award)和1998年美国国家技术奖(National Medal of Technol ...

  7. centos8/redhat8 无法上网,通过启动systemctl start NetworkManger搞定

    在systemd里面,可以直接使用systemctl进行管理 启动:systemctl start NetworkManger 关闭:systemctl stop NetworkManager 开机启 ...

  8. GraphX介绍

    转自:https://www.cnblogs.com/txq157/p/5978747.html 1.GraphX介绍 1.1 GraphX应用背景 Spark GraphX是一个分布式图处理框架,它 ...

  9. fastadmin 隐藏操作栏按钮

    formatter: function (value, row, index) { var that = $.extend({}, this); $(table).data({"operat ...

  10. 8.4.Zookeeper结构和命令

    1. Zookeeper结构 1.1.ZooKeeper数据模型Znode ZooKeeper拥有一个层次化的目录结构,命名符合常规文件系统规范 ZooKeeper树中的每个节点被称为—Znode,和 ...