美文网首页Spring
Eureka Server启动过程

Eureka Server启动过程

作者: 王勇1024 | 来源:发表于2019-07-14 11:54 被阅读0次

    前言

    我们已经在 Spring Cloud注册发现:Eureka 一文中对Eureka的基本原理有所了解,并在 Eureka使用示例 一文中我们简单介绍了如何创建一个 Eureka Server 和 Client,并将Client注册到Server中。其中有介绍到:@EnableEurekaServer 注解开启Eureka Server的服务注册功能。下面我们就以@EnableEurekaServer 为入口,来分析一下Eureka Server的启动流程。

    源码解析

    EnableEurekaServer注解位于 org.springframework.cloud.netflix.eureka.server 包中

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(EurekaServerMarkerConfiguration.class)
    public @interface EnableEurekaServer {
    }
    

    可以看到它导入了EurekaServerMarkerConfiguration类:

    @Configuration
    public class EurekaServerMarkerConfiguration {
        @Bean
        public Marker eurekaServerMarkerBean() {
            return new Marker();
        }
        class Marker {
        }
    }
    

    这个EurekaServerMarkerConfiguration会往Spring容器中注入一个eurekaServerMarkerBean,这个Marker是一个空类,那在这里起到什么作用呢?
    仔细查看这个类的注释,可以看到它只是一个开关标记,用来激活EurekaServerAutoConfiguration类的。

    那Eureka Server真正的启动入口在哪儿呢?
    了解 Spring Factories 机制的同学可能会想到去查看 spring-cloud-starter-netflix-eureka-server jar包中的 META-INF/spring.factories 文件,内容如下:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
    

    可以看到 Eureka Server 启动的真正入口是 EurekaServerAutoConfiguration

    @Configuration
    @Import(EurekaServerInitializerConfiguration.class)
    @ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
    @EnableConfigurationProperties({ EurekaDashboardProperties.class,
            InstanceRegistryProperties.class })
    @PropertySource("classpath:/eureka/server.properties")
    public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter
    
    • @Configuration注解表示这是一个配置类,通过@Bean注解声明一些注入到Spring IOC容器中的Bean。
    • @ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class),表示只要Spring容器中有EurekaServerMarkerConfiguration.Marker.class类的实例存在,那么就会将这个EurekaServerAutoConfiguration也注入到Spring容器中。
    • @Import(EurekaServerInitializerConfiguration.class)表明它导入了EurekaServerInitializerConfiguration这个类。
      此外,这个EurekaServerAutoConfiguration继承自WebMvcConfigurer,可以用来定义Spring MVC的一些配置。

    在这个类中,我们并没有发现与启动Eureka相关的代码,那么我们来看看它引入的这个EurekaServerInitializerConfiguration:

    @Configuration
    public class EurekaServerInitializerConfiguration
            implements ServletContextAware, SmartLifecycle, Ordered 
    

    可以看到,这也是一个配置类,同时它实现了 ServletContextAware接口,可以在Servlet容器启动后得到ServletContext容器上下文;它还实现了 SmartLifecycle
    ,这样在spring 生命周期中会调用这个类相关的方法。比如在spring初始化时,会调用它start方法。

        @Override
        public void start() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //TODO: is this class even needed now?
                        eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext);
                        log.info("Started Eureka Server");
                        publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
                        EurekaServerInitializerConfiguration.this.running = true;
                        publish(new EurekaServerStartedEvent(getEurekaServerConfig()));
                    }
                    catch (Exception ex) {
                        // Help!
                        log.error("Could not initialize Eureka servlet context", ex);
                    }
                }
            }).start();
        }
    

    contextInitialized方法是真正执行了Eureka Server的初始化过程:

        public void contextInitialized(ServletContext context) {
            try {
                initEurekaEnvironment();
                initEurekaServerContext();
    
                context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);
            }
            catch (Throwable e) {
                log.error("Cannot bootstrap eureka server :", e);
                throw new RuntimeException("Cannot bootstrap eureka server :", e);
            }
        }
    

    initEurekaEnvironment() 方法从配置文件中读取并设置datacenter和environment的配置参数,过程比较简单就不再赘述。
    initEurekaServerContext() 方法初始化 EurekaServer 上下文:

        protected void initEurekaServerContext() throws Exception {
            // 省略AWS相关逻辑
            EurekaServerContextHolder.initialize(this.serverContext);
            log.info("Initialized server context");
            // 从邻近的Eureka Server同步注册信息
            int registryCount = this.registry.syncUp();
            this.registry.openForTraffic(this.applicationInfoManager, registryCount);
            // 注册所有监控统计信息
            EurekaMonitors.registerAllStats();
        }
    

    registry.syncUp() 方法用于在当前 Eureka Server 节点启动时从邻近的Eureka Server同步注册信息,并返回同步得到的应用数量。当存在多个Eureka Server时,该方法会有实际的作用,用于达到各个节点之间数据的最终一致性。可以通过 eureka.server.registry-sync-retries 配置调整同步重试次数。如果未获取到应用实例,则 Eureka-Server 会有一段时间( 默认:5 分钟,可配 )不允许被 Eureka-Client 获取注册信息,避免影响 Eureka-Client 。详细过程请参考:Eureka-Server 集群同步

    registry.openForTraffic() 方法
    在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。

        @Override
        public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {
            // 计算每分钟最小续租次数。例如,如果30s续租一次,
           // 那么每分钟最小次数为2。
            this.expectedNumberOfClientsSendingRenews = count;
            updateRenewsPerMinThreshold();
            logger.info("Got {} instances from neighboring DS node", count);
            logger.info("Renew threshold is: {}", numberOfRenewsPerMinThreshold);
            this.startupTime = System.currentTimeMillis();
            if (count > 0) {
                this.peerInstancesTransferEmptyOnStartup = false;
            }
            DataCenterInfo.Name selfName = applicationInfoManager.getInfo().getDataCenterInfo().getName();
            // 如果数据中心为 AWS,则为所有副本启动AWS连接
            boolean isAws = Name.Amazon == selfName;
            if (isAws && serverConfig.shouldPrimeAwsReplicaConnections()) {
                logger.info("Priming AWS connections for all replicas..");
                primeAwsReplicas(applicationInfoManager);
            }
            logger.info("Changing status to UP");
            // 更新实例状态为UP
         
       applicationInfoManager.setInstanceStatus(InstanceStatus.UP);
            super.postInit();
        }
    

    此时,Eureka Server已经启动,开始接受Eureka Client的注册、续租以及查询请求。为了保证Eureka Client的健康存活,Eureka Server会启动定时任务清理租约信息,淘汰失效的Eureka Client。

        protected void postInit() {
            // 启动定时任务,定期清理租约信息
            renewsLastMin.start();
            if (evictionTaskRef.get() != null) {
                evictionTaskRef.get().cancel();
            }
            // 
            evictionTaskRef.set(new EvictionTask());
            evictionTimer.schedule(evictionTaskRef.get(),
                    serverConfig.getEvictionIntervalTimerInMs(),
                    serverConfig.getEvictionIntervalTimerInMs());
        }
    

    相关文章

      网友评论

        本文标题:Eureka Server启动过程

        本文链接:https://www.haomeiwen.com/subject/zywvkctx.html