乐趣区

EurekaServer自动装配及启动流程解析

在开始本篇文章之前,我想你对 SpringCloud 和 SpringBoot 的基本使用已经比较熟悉了,如果不熟悉的话可以参考我之前写过的文章
本篇文章的源码基于 SpringBoot2.0,SpringCloud 的 Finchley.RELEASE

@EnableEurekaServer注解

我们知道,在使用 Eureka 作为注册中心的时候,我们会在启动类中增加一个 @EnableEurekaServer 注解,这个注解我们是一个自定义的 EnableXXX 系列的注解,主要作用我们之前也多次提到了,就是引入配置类而已。看一下源码吧

@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 {}}

现在看这里好像难以理解,这是啥意思,搞个空的类干啥的,不要着急,接着往下看

自动装配

既然注解上没有找到我们想要的东西,那么就看一下 spring.factories 文件吧,这里自动配置的实现类是EurekaServerAutoConfiguration

由于这个类涉及的代码实在是太多了,这里就不贴了,咱们直接来解析这个类:

1. 引入 EurekaServerInitializerConfiguration 类

看名字就知道了这个类是负责 Eureka 的初始化工作的,这个类实现了 SmartLifecycle 接口,所以在 spring 初始化和销毁的时候,就会分别调用它的 start 和 stop 方法

首先看一下 start 方法

public void start() {new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 启动 EurekaServer
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();}

这个代码好像比较直接了当啊,直接就起个线程启动了 EurekaServer,然后发布了一些启动事件,来看启动的过程吧


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);
                }
        }

这里一共包含初始化环境和初始化上下文两个分支

初始化执行环境

这个不是很重要,可以过滤掉


protected void initEurekaEnvironment() throws Exception {log.info("Setting the eureka configuration..");
               //AWS 相关的东西,可以忽略
                String dataCenter = ConfigurationManager.getConfigInstance()
                                .getString(EUREKA_DATACENTER);
                if (dataCenter == null) {
                        log.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);
                }
        // 设置 Eureka 环境,默认为 test
                String environment = ConfigurationManager.getConfigInstance()
                                .getString(EUREKA_ENVIRONMENT);
                if (environment == null) {ConfigurationManager.getConfigInstance()
                                        .setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST);
                        log.info("Eureka environment value eureka.environment is not set, defaulting to test");
                }
                else {ConfigurationManager.getConfigInstance()
                                        .setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, environment);
                }
        }
初始化上下文

protected void initEurekaServerContext() throws Exception {
                // 设置 json 与 xml 序列化工具
                JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
                                XStream.PRIORITY_VERY_HIGH);
                XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
                                XStream.PRIORITY_VERY_HIGH);

                if (isAws(this.applicationInfoManager.getInfo())) {
                        this.awsBinder = new AwsBinderDelegate(this.eurekaServerConfig,
                                        this.eurekaClientConfig, this.registry, this.applicationInfoManager);
                        this.awsBinder.start();}

                EurekaServerContextHolder.initialize(this.serverContext);

                log.info("Initialized server context");

                // 同步 Eureka 集群数据
                int registryCount = this.registry.syncUp();
                this.registry.openForTraffic(this.applicationInfoManager, registryCount);

                // 注册监控统计信息
                EurekaMonitors.registerAllStats();}

这个方法中同步集群数据和注册监控信息都涉及的内容比较多,所以本篇文章就不再展开了,请关注我留意后续文章

@ConditionalOnBean({Marker.class})

看到这里就揭开了开篇 @EnableEurekaServer 注解注入的那个 bean 的含义了。也就是说如果咱们的启动类没有使用 @EnableEurekaServer 注解的话,这个自动配置类就不会执行,那也就没有 Eureka 的事了

@EnableConfigurationProperties({EurekaDashboardProperties.class, InstanceRegistryProperties.class})

深入这个注解发现这个还是使用的 @Import 注解的机制引入了两个类,这个注解在之前的源码解析文章中也多次提到了,这里就不展开了

EurekaDashboardProperties这个类比较简单,主要是 Eureka 的控制台的相关配置

// 控制台默认路径
private String path = "/";
// 是否开启控制台
private boolean enabled = true;

InstanceRegistryProperties,这个类是控制 Eureka 的注册时的配置信息

    // 每分钟续约次数
    @Value("${eureka.server.expectedNumberOfRenewsPerMin:1}") 
    private int expectedNumberOfRenewsPerMin = 1;
    // 默认打开的通信数量
    @Value("${eureka.server.defaultOpenForTrafficCount:1}")
    private int defaultOpenForTrafficCount = 1;
@PropertySource(“classpath:/eureka/server.properties”)

相信大家比较熟悉这个注解,加载 Eureka 的配置文件而已

配置文件中也仅仅只包含这个信息

spring.http.encoding.force=false
自动注入的 bean

EurekaServerAutoConfiguration类上几个注解就解析完了,接着看一下这个类中注入的几个比较重要的类吧

配置类 EurekaServerConfigBeanConfiguration

EurekaServerConfig
如果当前应用允许注册到其他 Eureka 服务中时,也就是属性 eureka.client.fetch-registry 为 true 时。就设置属性 registrySyncRetries 的值为 5,这个属性的意思是当 Eureka 服务器启动时尝试去获取集群里其他服务器上的注册信息的次数

EurekaController

这个就是 Eureka 自己的 controller 了,控制台的相关信息就是从这里获取的

ServerCodecs

设置 Eureka 的序列化工具

PeerAwareInstanceRegistry

集群注册信息同步相关的类,请期待后续深入解析文章

FilterRegistrationBean

EurekaServer 接受请求的一个拦截器,感兴趣的同学可以研究一下

退出移动版