文章首发于公众号《程序员果果》

地址 : https://mp.weixin.qq.com/s/FfJrAGQuHyVrsedtbr0Ihw

简介

上一篇文章《Eureka 源码分析之 Eureka Client》 通过源码知道 ,eureka Client 是通过 http rest来 与 eureka server 交互,实现 注册服务,续约服务,服务下线 等。本篇探究下eureka server。

源码分析

从 @EnableEurekaServer 注解为入口分析,通过源码可以看出他是一个标记注解:

  1. /**
  2. * Annotation to activate Eureka Server related configuration {@link
  3. */
  4. @Target(ElementType.TYPE)
  5. @Retention(RetentionPolicy.RUNTIME)
  6. @Documented
  7. @Import(EurekaServerMarkerConfiguration.class)
  8. public @interface EnableEurekaServer {
  9. }

从注释可以知道,用来激活 eureka server 的 配置类 EurekaServerAutoConfiguration 中相关配置,EurekaServerAutoConfiguration 的关键代码如下:

  1. @Configuration
  2. @Import(EurekaServerInitializerConfiguration.class)
  3. @ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
  4. @EnableConfigurationProperties({ EurekaDashboardProperties.class,
  5. InstanceRegistryProperties.class })
  6. @PropertySource("classpath:/eureka/server.properties")
  7. public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {
  8. /**
  9. * List of packages containing Jersey resources required by the Eureka server
  10. */
  11. private static final String[] EUREKA_PACKAGES = new String[] { "com.netflix.discovery",
  12. "com.netflix.eureka" };
  13. @Autowired
  14. private ApplicationInfoManager applicationInfoManager;
  15. @Autowired
  16. private EurekaServerConfig eurekaServerConfig;
  17. @Autowired
  18. private EurekaClientConfig eurekaClientConfig;
  19. @Autowired
  20. private EurekaClient eurekaClient;
  21. @Autowired
  22. private InstanceRegistryProperties instanceRegistryProperties;
  23. public static final CloudJacksonJson JACKSON_JSON = new CloudJacksonJson();
  24. @Bean
  25. public HasFeatures eurekaServerFeature() {
  26. return HasFeatures.namedFeature("Eureka Server",
  27. EurekaServerAutoConfiguration.class);
  28. }
  29. @Configuration
  30. protected static class EurekaServerConfigBeanConfiguration {
  31. // 创建并加载EurekaServerConfig的实现类,主要是Eureka-server的配置信息
  32. @Bean
  33. @ConditionalOnMissingBean
  34. public EurekaServerConfig eurekaServerConfig(EurekaClientConfig clientConfig) {
  35. EurekaServerConfigBean server = new EurekaServerConfigBean();
  36. if (clientConfig.shouldRegisterWithEureka()) {
  37. // Set a sensible default if we are supposed to replicate
  38. server.setRegistrySyncRetries(5);
  39. }
  40. return server;
  41. }
  42. }
  43. //加载EurekaController,SpringCloud 提供了一些额外的接口,用来获取eurekaServer的信息
  44. @Bean
  45. @ConditionalOnProperty(prefix = "eureka.dashboard", name = "enabled", matchIfMissing = true)
  46. public EurekaController eurekaController() {
  47. return new EurekaController(this.applicationInfoManager);
  48. }
  49. //省略 ...
  50. // 接收客户端的注册等请求就是通过InstanceRegistry来处理的,是真正处理业务的类,接下来会详细分析
  51. @Bean
  52. public PeerAwareInstanceRegistry peerAwareInstanceRegistry(
  53. ServerCodecs serverCodecs) {
  54. this.eurekaClient.getApplications(); // force initialization
  55. return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,
  56. serverCodecs, this.eurekaClient,
  57. this.instanceRegistryProperties.getExpectedNumberOfRenewsPerMin(),
  58. this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
  59. }
  60. //配置服务节点信息,这里的作用主要是为了配置Eureka的peer节点,也就是说当有收到有节点注册上来的时候,需要通知给哪些节点
  61. @Bean
  62. @ConditionalOnMissingBean
  63. public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry,
  64. ServerCodecs serverCodecs) {
  65. return new RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig,
  66. this.eurekaClientConfig, serverCodecs, this.applicationInfoManager);
  67. }
  68. //省略 ...
  69. //EurekaServer的上下文
  70. @Bean
  71. public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs,
  72. PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
  73. return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs,
  74. registry, peerEurekaNodes, this.applicationInfoManager);
  75. }
  76. // 初始化Eureka-server,会同步其他注册中心的数据到当前注册中心
  77. @Bean
  78. public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry,
  79. EurekaServerContext serverContext) {
  80. return new EurekaServerBootstrap(this.applicationInfoManager,
  81. this.eurekaClientConfig, this.eurekaServerConfig, registry,
  82. serverContext);
  83. }
  84. // 配置拦截器,ServletContainer里面实现了jersey框架,通过他来实现eurekaServer对外的restFull接口
  85. @Bean
  86. public FilterRegistrationBean jerseyFilterRegistration(
  87. javax.ws.rs.core.Application eurekaJerseyApp) {
  88. FilterRegistrationBean bean = new FilterRegistrationBean();
  89. bean.setFilter(new ServletContainer(eurekaJerseyApp));
  90. bean.setOrder(Ordered.LOWEST_PRECEDENCE);
  91. bean.setUrlPatterns(
  92. Collections.singletonList(EurekaConstants.DEFAULT_PREFIX + "/*"));
  93. return bean;
  94. }
  95. //省略 ...
  96. }

从EurekaServerAutoConfiguration 类上的注解@Import(EurekaServerInitializerConfiguration.class) 可以到,实例化类EurekaServerAutoConfiguration之前,已经实例化了EurekaServerInitializerConfiguration类,代码如下:

  1. @Configuration
  2. @CommonsLog
  3. public class EurekaServerInitializerConfiguration
  4. implements ServletContextAware, SmartLifecycle, Ordered {
  5. // 此处省略部分代码
  6. @Override
  7. public void start() {
  8. // 启动一个线程
  9. new Thread(new Runnable() {
  10. @Override
  11. public void run() {
  12. try {
  13. //初始化EurekaServer,同时启动Eureka Server ,后面着重讲这里
  14. eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext);
  15. log.info("Started Eureka Server");
  16. // 发布EurekaServer的注册事件
  17. publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
  18. // 设置启动的状态为true
  19. EurekaServerInitializerConfiguration.this.running = true;
  20. // 发送Eureka Start 事件 , 其他还有各种事件,我们可以监听这种时间,然后做一些特定的业务需求,后面会讲到。
  21. publish(new EurekaServerStartedEvent(getEurekaServerConfig()));
  22. }
  23. catch (Exception ex) {
  24. // Help!
  25. log.error("Could not initialize Eureka servlet context", ex);
  26. }
  27. }
  28. }).start();
  29. }
  30. // 此处省略部分代码
  31. }

这个start方法中开启了一个新的线程,然后进行一些Eureka Server的初始化工作,比如调用eurekaServerBootstrap的contextInitialized方法,EurekaServerBootstrap代码如下:

  1. public class EurekaServerBootstrap {
  2. // 此处省略部分代码
  3. public void contextInitialized(ServletContext context) {
  4. try {
  5. // 初始化Eureka的环境变量
  6. initEurekaEnvironment();
  7. // 初始化Eureka的上下文
  8. initEurekaServerContext();
  9. context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);
  10. }
  11. catch (Throwable e) {
  12. log.error("Cannot bootstrap eureka server :", e);
  13. throw new RuntimeException("Cannot bootstrap eureka server :", e);
  14. }
  15. }
  16. protected void initEurekaEnvironment() throws Exception {
  17. log.info("Setting the eureka configuration..");
  18. String dataCenter = ConfigurationManager.getConfigInstance()
  19. .getString(EUREKA_DATACENTER);
  20. if (dataCenter == null) {
  21. log.info(
  22. "Eureka data center value eureka.datacenter is not set, defaulting to default");
  23. ConfigurationManager.getConfigInstance()
  24. .setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, DEFAULT);
  25. }
  26. else {
  27. ConfigurationManager.getConfigInstance()
  28. .setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, dataCenter);
  29. }
  30. String environment = ConfigurationManager.getConfigInstance()
  31. .getString(EUREKA_ENVIRONMENT);
  32. if (environment == null) {
  33. ConfigurationManager.getConfigInstance()
  34. .setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST);
  35. log.info(
  36. "Eureka environment value eureka.environment is not set, defaulting to test");
  37. }
  38. else {
  39. ConfigurationManager.getConfigInstance()
  40. .setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, environment);
  41. }
  42. }
  43. protected void initEurekaServerContext() throws Exception {
  44. // For backward compatibility
  45. JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
  46. XStream.PRIORITY_VERY_HIGH);
  47. XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
  48. XStream.PRIORITY_VERY_HIGH);
  49. if (isAws(this.applicationInfoManager.getInfo())) {
  50. this.awsBinder = new AwsBinderDelegate(this.eurekaServerConfig,
  51. this.eurekaClientConfig, this.registry, this.applicationInfoManager);
  52. this.awsBinder.start();
  53. }
  54. //初始化eureka server上下文
  55. EurekaServerContextHolder.initialize(this.serverContext);
  56. log.info("Initialized server context");
  57. // Copy registry from neighboring eureka node
  58. // 从相邻的eureka节点复制注册表
  59. int registryCount = this.registry.syncUp();
  60. // 默认每30秒发送心跳,1分钟就是2次
  61. // 修改eureka状态为up
  62. // 同时,这里面会开启一个定时任务,用于清理 60秒没有心跳的客户端。自动下线
  63. this.registry.openForTraffic(this.applicationInfoManager, registryCount);
  64. // Register all monitoring statistics.
  65. EurekaMonitors.registerAllStats();
  66. }
  67. public void contextDestroyed(ServletContext context) {
  68. try {
  69. log.info("Shutting down Eureka Server..");
  70. context.removeAttribute(EurekaServerContext.class.getName());
  71. destroyEurekaServerContext();
  72. destroyEurekaEnvironment();
  73. }
  74. catch (Throwable e) {
  75. log.error("Error shutting down eureka", e);
  76. }
  77. log.info("Eureka Service is now shutdown...");
  78. }
  79. }

在初始化Eureka Server上下文环境后,就会继续执行openForTraffic方法,这个方法主要是设置了期望每分钟接收到的心跳次数,并将服务实例的状态设置为UP,最后又通过方法postInit来开启一个定时任务,用于每隔一段时间(默认60秒)将没有续约的服务实例(默认90秒没有续约)清理掉。openForTraffic的方法代码如下:

  1. @Override
  2. public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {
  3. // Renewals happen every 30 seconds and for a minute it should be a factor of 2.
  4. // 计算每分钟最大续约数
  5. this.expectedNumberOfRenewsPerMin = count * 2;
  6. // 计算每分钟最小续约数
  7. this.numberOfRenewsPerMinThreshold =
  8. (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
  9. logger.info("Got {} instances from neighboring DS node", count);
  10. logger.info("Renew threshold is: {}", numberOfRenewsPerMinThreshold);
  11. this.startupTime = System.currentTimeMillis();
  12. if (count > 0) {
  13. this.peerInstancesTransferEmptyOnStartup = false;
  14. }
  15. DataCenterInfo.Name selfName = applicationInfoManager.getInfo().getDataCenterInfo().getName();
  16. boolean isAws = Name.Amazon == selfName;
  17. if (isAws && serverConfig.shouldPrimeAwsReplicaConnections()) {
  18. logger.info("Priming AWS connections for all replicas..");
  19. primeAwsReplicas(applicationInfoManager);
  20. }
  21. logger.info("Changing status to UP");
  22. // 修改服务实例的状态为UP
  23. applicationInfoManager.setInstanceStatus(InstanceStatus.UP);
  24. // 开启定时任务,每隔一段时间(默认60秒)将没有续约的服务实例(默认90秒没有续约)清理掉
  25. super.postInit();
  26. }

postInit方法开启了一个新的定时任务,代码如下:

  1. protected void postInit() {
  2. renewsLastMin.start();
  3. if (evictionTaskRef.get() != null) {
  4. evictionTaskRef.get().cancel();
  5. }
  6. evictionTaskRef.set(new EvictionTask());
  7. evictionTimer.schedule(evictionTaskRef.get(),
  8. serverConfig.getEvictionIntervalTimerInMs(),
  9. serverConfig.getEvictionIntervalTimerInMs());
  10. }

这里的时间间隔都来自于EurekaServerConfigBean类,可以在配置文件中以eureka.server开头的配置来进行设置。

参考

https://www.e-learn.cn/content/qita/775244/

https://nobodyiam.com/2016/06/25/dive-into-eureka/

https://blog.csdn.net/Lammonpeter/article/details/84330900

欢迎扫码或微信搜索公众号《程序员果果》关注我,关注有惊喜~

Eureka 源码分析之 Eureka Server的更多相关文章

  1. Eureka 源码分析之 Eureka Client

    文章首发于微信公众号<程序员果果> 地址:https://mp.weixin.qq.com/s/47TUd96NMz67_PCDyvyInQ 简介 Eureka是一种基于REST(Repr ...

  2. Eureka源码分析:Eureka不会进行二次Replication的原因

    Eureka不会进行二次同步注册信息 Eureka会将本实例中的注册信息同步到它的peer节点上,这是我们都知道的特性.然而,当peer节点收到同步数据后,并不会将这些信息再同步到它自己的peer节点 ...

  3. 【一起学源码-微服务】Nexflix Eureka 源码十三:Eureka源码解读完结撒花篇~!

    前言 想说的话 [一起学源码-微服务-Netflix Eureka]专栏到这里就已经全部结束了. 实话实说,从最开始Eureka Server和Eureka Client初始化的流程还是一脸闷逼,到现 ...

  4. 微服务之SpringCloud实战(四):SpringCloud Eureka源码分析

    Eureka源码解析: 搭建Eureka服务的时候,我们会再SpringBoot启动类加上@EnableEurekaServer的注解,这个注解做了一些什么,我们一起来看. 点进@EnableEure ...

  5. Eureka源码分析

    源码流程图 先上图,不太清晰,抱歉 一.Eureka Server源码分析 从@EnableEurekaServer注解为入口,它是一个标记注解,点进去看 注解内容如下 /** * 激活Eureka服 ...

  6. 【SpringCloud技术专题】「Eureka源码分析」从源码层面让你认识Eureka工作流程和运作机制(上)

    前言介绍 了解到了SpringCloud,大家都应该知道注册中心,而对于我们从过去到现在,SpringCloud中用的最多的注册中心就是Eureka了,所以深入Eureka的原理和源码,接下来我们要进 ...

  7. 【Canal源码分析】Canal Server的启动和停止过程

    本文主要解析下canal server的启动过程,希望能有所收获. 一.序列图 1.1 启动 1.2 停止 二.源码分析 整个server启动的过程比较复杂,看图难以理解,需要辅以文字说明. 首先程序 ...

  8. 【源码系列】Eureka源码分析

    对于服务注册中心.服务提供者.服务消费者这个三个主要元素来说,服务提供者和服务消费者(即Eureka客户端)在整个运行机制中是大部分通信行为的主动发起者(服务注册.续约.下线等),而注册中心主要是处理 ...

  9. Spring Cloud Eureka源码分析 --- client 注册流程

    Eureka Client 是一个Java 客户端,用于简化与Eureka Server的交互,客户端同时也具备一个内置的.使用轮询负载算法的负载均衡器. 在应用启动后,将会向Eureka Serve ...

随机推荐

  1. springboot项目作为其他项目子项目

    <?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/P ...

  2. spark2.1.0 自定义AccumulatorV2累加少值(线程不安全)?

    一.踩坑经历 自定义的accumulator是线程不安全的,会造成累加结果不正确.自定找了很久没想到是线程不安全行成的. 二.解决方法 创建一个线程安全的集合变量(我用的是Java的Concurren ...

  3. nodejs中 require 方法的加载规则

    require参数类型 http.fs.path等,原生模块 ./mod或../mod,相对路径的文件模块 /pathtomodule/mod,绝对路径的文件模块 mod,非原生模块的文件模块 在进 ...

  4. 对ECMAScript的研究-----------引用

    ECMAScript 新特性与标准提案 一:ES 模块 第一个要介绍的 ES 模块,由于历史上 JavaScript 没有提供模块系统,在远古时期我们常用多个 script 标签将代码进行人工隔离.但 ...

  5. JavaWeb面试篇(7)

    61,JDBC访问数据库的基本步骤是什么?1,加载驱动2,通过DriverManager对象获取连接对象Connection3,通过连接对象获取会话4,通过会话进行数据的增删改查,封装对象5,关闭资源 ...

  6. 一张图解释Linux文件系统中硬链接和软链接的区别

    如图所示,硬链接与原始文件共用一个inode,但inode是不跨文件系统的(Ext3.Ext4),每个文件系统都有自己的inode列表.因此,硬链接是没办法跨文件系统的 而软链接不同,软链接相当于重新 ...

  7. ef 分页

    public List<TEntity> FindList(Expression<Func<TEntity, bool>> predicate, Paginatio ...

  8. oracle 7.4安装nvidia驱动

    2019-8-28 参考网页: 如何在k8s集群中安装nvidia.cuda并使用GPU进行训练 https://blog.csdn.net/u013042928/article/details/78 ...

  9. XML to HTML

    本章讲解如何把 XML 数据显示为 HTML. 在 HTML 中显示 XML 数据 在上一节中,我们讲解了如何通过 JavaScript 来解析 XML 并访问 DOM. 本例遍历一个 XML 文件 ...

  10. tf.random_uniform出错tensorflow2.0出错

    https://blog.csdn.net/hhy_csdn/article/details/82263007 https://blog.csdn.net/weixin_44038165/articl ...