spring cloud eureka的初始化

概述

一直关注spring cloud,以后微服务必将其发扬光大,所以结合源码和网上的文章详细研究一下。

本文主要是分析eureka(EurekaClient)的初始化过程。当需要使用EurekaClient的时候,会在SpringBootApplication上增加EnableEurekaClient注解,那么我们就从spring boot项目中的EnableEurekaClient注解开始(EurekaServer启动过程也类似)。

1. 加载过程

1.1 分析过程

EnableEurekaClient ->
EnableDiscoveryClient ->
EnableDiscoveryClientImportSelector ->
EnableDiscoveryClientImportSelector(selectImports方法) ->
SpringFactoriesLoader(loadFactoryNames添加spring.factories下的类,spring启动初始化这些类)

1.2 spring 启动过程

spring refresh ->
annotation.ConfigurationClassParser.processImports() ->
EurekaClientAutoConfiguration ->
DiscoveryClient(spring boot 包) ->
CloudEurekaClient(eureka包)

2. EnableEurekaClient 注解

    @EnableDiscoveryClient
    public @interface EnableEurekaClient

3. EnableDiscoveryClient 注解

    Import(EnableDiscoveryClientImportSelector.class)
    public @interface EnableDiscoveryClient

4. EnableDiscoveryClientImportSelector

该类会调用父类的方法selectImports,父类中的selectImports会调用spring的loadFactoryNames,将配置文件下的类加载,先放到指定的map中等待spring初始化,实际初始化这些类是在org.springframework.context.annotation.ConfigurationClassParser.processImports()

4.1 EnableDiscoveryClientImportSelector

    @Order(Ordered.LOWEST_PRECEDENCE - 100)
    public class EnableDiscoveryClientImportSelector
            extends SpringFactoryImportSelector<EnableDiscoveryClient> {

        @Override
        public String[] selectImports(AnnotationMetadata metadata) {
            String[] imports = super.selectImports(metadata);

            AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                    metadata.getAnnotationAttributes(getAnnotationClass().getName(), true));

            boolean autoRegister = attributes.getBoolean("autoRegister");

            if (autoRegister) {
                List<String> importsList = new ArrayList<>(Arrays.asList(imports));
                importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
                imports = importsList.toArray(new String[0]);
            }

            return imports;
        }

        @Override
        protected boolean isEnabled() {
            return new RelaxedPropertyResolver(getEnvironment()).getProperty(
                    "spring.cloud.discovery.enabled", Boolean.class, Boolean.TRUE);
        }

        @Override
        protected boolean hasDefaultFactory() {
            return true;
        }
    }

4.2 SpringFactoryImportSelector

EnableDiscoveryClientImportSelector的父类,调用SpringFactoriesLoader.loadFactoryNames加载配置文件下的类。

    @Override
    public String[] selectImports(AnnotationMetadata metadata) {
        if (!isEnabled()) {
            return new String[0];
        }
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                metadata.getAnnotationAttributes(this.annotationClass.getName(), true));

        Assert.notNull(attributes, "No " + getSimpleName() + " attributes found. Is "
                + metadata.getClassName() + " annotated with @" + getSimpleName() + "?");

        // Find all possible auto configuration classes, filtering duplicates
        List<String> factories = new ArrayList<>(new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(this.annotationClass,    this.beanClassLoader)));

        if (factories.isEmpty() && !hasDefaultFactory()) {
            throw new IllegalStateException("Annotation @" + getSimpleName()
                    + " found, but there are no implementations. Did you forget to include a starter?");
        }

        if (factories.size() > 1) {
            // there should only ever be one DiscoveryClient, but there might be more than
            // one factory
            log.warn("More than one implementation " + "of @" + getSimpleName()
                    + " (now relying on @Conditionals to pick one): " + factories);
        }

        return factories.toArray(new String[factories.size()]);
    }

4.3 SpringFactoriesLoader

该类的方法loadFactoryNames,将FACTORIES_RESOURCE_LOCATION下的类添加到一个className的列表中。

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

    public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        try {
            Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            List<String> result = new ArrayList<String>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                String factoryClassNames = properties.getProperty(factoryClassName);
                result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
            }
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
                    "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

5. EurekaClient的初始化

** eureka包中resource下的spring.factories **

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
    org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
    org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
    org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration

EurekaClientAutoConfiguration 会初始化EurekaClientConfigBean、 EurekaInstanceConfigBean和DiscoveryClient(DiscoveryClient实际是对eurake中的DiscoveryClient的封装)

5.1 EurekaClientAutoConfiguration

该类中初始化了EurekaClientConfigBean、 EurekaInstanceConfigBean和DiscoveryClient

** EurekaClientConfigBean类 **

    @Bean
    @ConditionalOnMissingBean(value = EurekaClientConfig.class, search = SearchStrategy.CURRENT)
    public EurekaClientConfigBean eurekaClientConfigBean() {
        EurekaClientConfigBean client = new EurekaClientConfigBean();
        if ("bootstrap".equals(this.env.getProperty("spring.config.name"))) {
            // We don't register during bootstrap by default, but there will be another
            // chance later.
            client.setRegisterWithEureka(false);
        }
        return client;
    }

** EurekaInstanceConfigBean类 **

    @Bean
    @ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)
    public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils) {
        EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
        instance.setNonSecurePort(this.nonSecurePort);
        instance.setInstanceId(getDefaultInstanceId(this.env));

        if (this.managementPort != this.nonSecurePort && this.managementPort != 0) {
            if (StringUtils.hasText(this.hostname)) {
                instance.setHostname(this.hostname);
            }
            RelaxedPropertyResolver relaxedPropertyResolver = new RelaxedPropertyResolver(env, "eureka.instance.");
            String statusPageUrlPath = relaxedPropertyResolver.getProperty("statusPageUrlPath");
            String healthCheckUrlPath = relaxedPropertyResolver.getProperty("healthCheckUrlPath");
            if (StringUtils.hasText(statusPageUrlPath)) {
                instance.setStatusPageUrlPath(statusPageUrlPath);
            }
            if (StringUtils.hasText(healthCheckUrlPath)) {
                instance.setHealthCheckUrlPath(healthCheckUrlPath);
            }
            String scheme = instance.getSecurePortEnabled() ? "https" : "http";
            instance.setStatusPageUrl(scheme + "://" + instance.getHostname() + ":"
                    + this.managementPort + instance.getStatusPageUrlPath());
            instance.setHealthCheckUrl(scheme + "://" + instance.getHostname() + ":"
                    + this.managementPort + instance.getHealthCheckUrlPath());
        }
        return instance;
    }

** DiscoveryClient类 **

    @Bean
    public DiscoveryClient discoveryClient(EurekaInstanceConfig config,
            EurekaClient client) {
        return new EurekaDiscoveryClient(config, client);
    }

5.2 EurekaClient的创建

在EurekaClientAutoConfiguration中的静态类EurekaClientConfiguration中,创建了CloudEurekaClient。

    @Bean(destroyMethod = "shutdown")
    @ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
    public EurekaClient eurekaClient(ApplicationInfoManager manager,
            EurekaClientConfig config) {
        return new CloudEurekaClient(manager, config, this.optionalArgs,
                this.context);
}

5.3 CloudEurekaClient

这个类实际继承了 DiscoveryClient(eureka-client包中的),其实spring boot中的EurekaClient最终也就是对eurekaClient(eureka-client)的封装

    public CloudEurekaClient(ApplicationInfoManager applicationInfoManager,
                             EurekaClientConfig config,
                             DiscoveryClientOptionalArgs args,
                             ApplicationEventPublisher publisher) {
        super(applicationInfoManager, config, args);
        this.applicationInfoManager = applicationInfoManager;
        this.publisher = publisher;
        this.eurekaTransportField = ReflectionUtils.findField(DiscoveryClient.class, "eurekaTransport");
        ReflectionUtils.makeAccessible(this.eurekaTransportField);
    }

6. 实例化

spring启动的时候,调用spring的refresh(AbstractApplicationContext),fresh中会调用西面的方法来进行对imports的类进行实例化。

    org.springframework.context.annotation.ConfigurationClassParser.processImports()

参考:http://www.idouba.net/spring-cloud-source-load-eureka-client-by-annotation/

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,079评论 19 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,985评论 6 342
  • 1.1 spring IoC容器和beans的简介 Spring 框架的最核心基础的功能是IoC(控制反转)容器,...
    simoscode阅读 6,758评论 2 22
  • 总有那么一个人,对于你来说,就好似一束光。你踫不到,却能照进你的心底。很庆幸,在学生时代,我遇到了。 最初,我并不...
    开心果kkw阅读 324评论 0 0
  • 文/陈慕 你说的每句话我都相信, 我等着, 等着我的相信。 后来, 我冲自己轻轻一笑。 把那信任踩到了脚底。
    我是陈慕阅读 173评论 0 3