美文网首页
spring cloud eureka的初始化

spring cloud eureka的初始化

作者: 哒哒哒哒哈哈 | 来源:发表于2018-09-05 13:16 被阅读0次

    概述

    一直关注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 eureka的初始化

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