spring cloud 服务发现之Eureka Client(二)—自动装配

spring cloud 服务发现之Eureka Client(一)—客户端配置DEMO章节中介绍了如何快速的启动一个Eureka Client, 并将当前服务信息注册到Eureka Server中。在这篇文章中,将主要介绍在Eureka Client自动状态的过程中,需要执行那些过程,那些类是在启动过程中比不可少了。

这篇文章中,主要介绍通用的使用过程,其他的装配过程基本类似。

EnableDiscoveryClient

该类作为通用的服务发现客户端的启用类,可以用作不同的服务发现组件。该类源码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient {

    /**
     * If true, the ServiceRegistry will automatically register the local server.
     * @return - {@code true} if you want to automatically register.
     */
    boolean autoRegister() default true;

}

通过该类的源码可以看到,该类型通过Import引入了EnableDiscoveryClientImportSelector类型,该类型主要是对EnableDiscoveryClient 注解的解析和配置。

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

                // 判断是否auto register 
        boolean autoRegister = attributes.getBoolean("autoRegister");

        if (autoRegister) {
            List<String> importsList = new ArrayList<>(Arrays.asList(imports));
                        // 自动引入AutoServiceRegistrationConfiguration
            importsList.add(
                    "org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
            imports = importsList.toArray(new String[0]);
        }
        else {
            Environment env = getEnvironment();
            if (ConfigurableEnvironment.class.isInstance(env)) {
                ConfigurableEnvironment configEnv = (ConfigurableEnvironment) env;
                LinkedHashMap<String, Object> map = new LinkedHashMap<>();
                map.put("spring.cloud.service-registry.auto-registration.enabled", false);
                MapPropertySource propertySource = new MapPropertySource(
                        "springCloudDiscoveryClient", map);
                configEnv.getPropertySources().addLast(propertySource);
            }

        }

        return imports;
    }

}

 

通过Import的方式,引入了新的配置AutoServiceRegistrationConfiguration类型。

AutoServiceRegistrationConfiguration

@Configuration
@EnableConfigurationProperties(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public class AutoServiceRegistrationConfiguration {

}

该类主要是判断spring.cloud.service-registry.auto-registration.enabled的配置信息是否启用服务自动注册功能,并引入配置类AutoServiceRegistrationProperties.

注解的功能整体到该类就截止了,整体并没有特殊的功能,主要就是加载必要的配置类型,生成Properties配置文件。

AutoServiceRegistrationAutoConfiguration

@Configuration
@Import(AutoServiceRegistrationConfiguration.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public class AutoServiceRegistrationAutoConfiguration {

    @Autowired(required = false)
    private AutoServiceRegistration autoServiceRegistration;

    @Autowired
    private AutoServiceRegistrationProperties properties;

    @PostConstruct
    protected void init() {
        if (this.autoServiceRegistration == null && this.properties.isFailFast()) {
            throw new IllegalStateException("Auto Service Registration has "
                    + "been requested, but there is no AutoServiceRegistration bean");
        }
    }

}

这是一个装配类型,该类型的启动,主要依赖于spring.cloud.service-registry.auto-registration.enabled配置的值,在缺省情况下为true。在该类中,主要会依赖注入两个类型,AutoServiceRegistrationPropertiesAutoServiceRegistration 类型,下面主要看下AutoServiceRegistration 类型,该类在什么地方被创建?

EurekaDiscoveryClientConfigServiceBootstrapConfiguration

在Spring boot 启动过程中会扫描spring.factories文件,并加载文件中的配置内容,在Spring cloud commons中,有如下配置:

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceBootstrapConfiguration

查看该类的配置源码:

@ConditionalOnClass(ConfigServicePropertySourceLocator.class)
@ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled", matchIfMissing = false)
@Configuration
@Import({ EurekaDiscoveryClientConfiguration.class, // this emulates
        // @EnableDiscoveryClient, the import
        // selector doesn't run before the
        // bootstrap phase
        EurekaClientAutoConfiguration.class })
public class EurekaDiscoveryClientConfigServiceBootstrapConfiguration {

}

该类被启动有两个条件:

  • ConfigServicePropertySourceLocator 类必须存在
  • spring.cloud.config.discovery.enabled 启动

当以上两个条件满足时, 该类才会启动配置, 在装配的过程中,主要设计到两个配置对象: EurekaDiscoveryClientConfigurationEurekaClientAutoConfiguration

EurekaDiscoveryClientConfiguration

查看该类源码:

@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@ConditionalOnDiscoveryEnabled
public class EurekaDiscoveryClientConfiguration {

    @Bean
    public Marker eurekaDiscoverClientMarker() {
        return new Marker();
    }

    @Configuration
    @ConditionalOnProperty(value = "eureka.client.healthcheck.enabled", matchIfMissing = false)
    protected static class EurekaHealthCheckHandlerConfiguration {

        @Autowired(required = false)
        private HealthAggregator healthAggregator = new OrderedHealthAggregator();

        @Bean
        @ConditionalOnMissingBean(HealthCheckHandler.class)
        public EurekaHealthCheckHandler eurekaHealthCheckHandler() {
            return new EurekaHealthCheckHandler(this.healthAggregator);
        }

    }

    class Marker {

    }

    @Configuration
    @ConditionalOnClass(RefreshScopeRefreshedEvent.class)
    protected static class EurekaClientConfigurationRefresher
            implements ApplicationListener<RefreshScopeRefreshedEvent> {

        @Autowired(required = false)
        private EurekaClient eurekaClient;

        @Autowired(required = false)
        private EurekaAutoServiceRegistration autoRegistration;

        public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
            // This will force the creation of the EurkaClient bean if not already created
            // to make sure the client will be reregistered after a refresh event
            if (eurekaClient != null) {
                eurekaClient.getApplications();
            }
            if (autoRegistration != null) {
                // register in case meta data changed
                this.autoRegistration.stop();
                this.autoRegistration.start();
            }
        }

    }

}

 

该装配类型生效,需要满足一下条件:

  • classpath中必须要引入EurekaClientConfig类型
  • 启用配置eureka.client.enabled, 默认值为true
  • 启用spring.cloud.config.discovery.enabled配置,默认值为true

该配置类型主要做了两件事情:

  • 生成Marker对象,在Eureka Server装配时,也是通过Marker对象来启动装配的,因此这个Marker也是作为开关使用
  • 根据eureka.client.healthcheck.enabled默认值为false,如果为true,则开启EurekaHealthCheckHandler 类型
  • 生成EurekaClientConfigurationRefresher Bean, 用于处理 RefreshScopeRefreshedEvent事件.

EurekaClientAutoConfiguration

@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@Import(DiscoveryClientOptionalArgsConfiguration.class)
@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@ConditionalOnDiscoveryEnabled
@AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,
        CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
@AutoConfigureAfter(name = {
        "org.springframework.cloud.autoconfigure.RefreshAutoConfiguration",
        "org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration",
        "org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration" })

首先需要关注该装配类型什么时候能够生效, 当前类型生效需要满足以下条件:

  • classpath环境中能够找到EurekaClientConfig类型
  • 需要依赖EurekaDiscoveryClientConfiguration 中生成Marker Bean对象。
  • 需要启用配置eureka.client.enabled,默认值为true
  • 需要启用配置 spring.cloud.discovery.enabled,默认为true

当环境中满足以上权限时,则EurekaClientAutoConfiguration自动装配开始,在装配开始前,主要包含了:

  • 引入DiscoveryClientOptionalArgsConfiguration配置类型
  • AutoConfigureBefore 配置了当前配置完成后,继续配置 NoopDiscoveryClientAutoConfigurationCommonsClientAutoConfigurationServiceRegistryAutoConfiguration装配类型
  • AutoConfigureAfter 装配开始前,则需要引入 RefreshAutoConfiguration, EurekaDiscoveryClientConfiguration, AutoServiceRegistrationAutoConfiguration 类型

DiscoveryClientOptionalArgsConfiguration

该类型从命名上可知,这是一个可选项,源码如下:

@Configuration
public class DiscoveryClientOptionalArgsConfiguration {

    @Bean
    @ConditionalOnMissingClass("com.sun.jersey.api.client.filter.ClientFilter")
    @ConditionalOnMissingBean(value = AbstractDiscoveryClientOptionalArgs.class, search = SearchStrategy.CURRENT)
    public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOptionalArgs() {
        return new RestTemplateDiscoveryClientOptionalArgs();
    }

    @Bean
    @ConditionalOnClass(name = "com.sun.jersey.api.client.filter.ClientFilter")
    @ConditionalOnMissingBean(value = AbstractDiscoveryClientOptionalArgs.class, search = SearchStrategy.CURRENT)
    public MutableDiscoveryClientOptionalArgs discoveryClientOptionalArgs() {
        return new MutableDiscoveryClientOptionalArgs();
    }

}

该类型主要是配置DiscoveryClient使用,在缺少AbstractDiscoveryClientOptionalArgs对象时,能够生成对应对象,用于存储参数信息。

前置装配 – RefreshAutoConfiguration

在装配开始前,将优先配置该类, 该类具体源码如下:

@Configuration
@ConditionalOnClass(RefreshScope.class)
@ConditionalOnProperty(name = RefreshAutoConfiguration.REFRESH_SCOPE_ENABLED, matchIfMissing = true)
@AutoConfigureBefore(HibernateJpaAutoConfiguration.class)
public class RefreshAutoConfiguration {

    /**
     * Name of the refresh scope name.
     */
    public static final String REFRESH_SCOPE_NAME = "refresh";

    /**
     * Name of the prefix for refresh scope.
     */
    public static final String REFRESH_SCOPE_PREFIX = "spring.cloud.refresh";

    /**
     * Name of the enabled prefix for refresh scope.
     */
    public static final String REFRESH_SCOPE_ENABLED = REFRESH_SCOPE_PREFIX + ".enabled";

    @Bean
    @ConditionalOnMissingBean(RefreshScope.class)
    public static RefreshScope refreshScope() {
        return new RefreshScope();
    }

    @Bean
    @ConditionalOnMissingBean
    public static LoggingRebinder loggingRebinder() {
        return new LoggingRebinder();
    }

    @Bean
    @ConditionalOnMissingBean
    public ContextRefresher contextRefresher(ConfigurableApplicationContext context,
            RefreshScope scope) {
        return new ContextRefresher(context, scope);
    }

    @Bean
    public RefreshEventListener refreshEventListener(ContextRefresher contextRefresher) {
        return new RefreshEventListener(contextRefresher);
    }

    @Configuration
    @ConditionalOnClass(name = "javax.persistence.EntityManagerFactory")
    protected static class JpaInvokerConfiguration implements LoadTimeWeaverAware {

        @Autowired
        private ListableBeanFactory beanFactory;

        @PostConstruct
        public void init() {
            String cls = "org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker";
            if (this.beanFactory.containsBean(cls)) {
                this.beanFactory.getBean(cls);
            }
        }

        @Override
        public void setLoadTimeWeaver(LoadTimeWeaver ltw) {
        }

    }
....

}

该类型主要是对spring cloud中RefreshScope相关的配置信息, 这里不错探讨

前置装配-EurekaDiscoveryClientConfiguration

该类前面已经详细说过,这里就不再讲述了

前置装配-AutoServiceRegistrationAutoConfiguration

该类前面也已经讲述过了,这里也不在讲述.

装配中-EurekaClientConfigBean

@Bean
    @ConditionalOnMissingBean(value = EurekaClientConfig.class, search = SearchStrategy.CURRENT)
    public EurekaClientConfigBean eurekaClientConfigBean(ConfigurableEnvironment env) {
        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;
    }

该类主要是实现EurekaClient配置对象,该类是基于EurekaClientConfig进行二次封装, 主要由spring实现。在properties中的eureka.client前缀配置信息,最终会被解析为当前对象。

装配中-ManagementMetadataProvider

@Bean
    @ConditionalOnMissingBean
    public ManagementMetadataProvider serviceManagementMetadataProvider() {
        return new DefaultManagementMetadataProvider();
    }

该类主要对Instance中的元数据信息进行管理,

装配中-InetUtils

这个类型是Spring提供的一个工具类,该类的自动注入是放在spring-cloud-commons模块中,通过UtilAutoConfiguration类型进行装配,源码如下:

@Configuration
@ConditionalOnProperty(value = "spring.cloud.util.enabled", matchIfMissing = true)
@AutoConfigureOrder(0)
@EnableConfigurationProperties
public class UtilAutoConfiguration {

    @Bean
    public InetUtilsProperties inetUtilsProperties() {
        return new InetUtilsProperties();
    }

    @Bean
    @ConditionalOnMissingBean
    public InetUtils inetUtils(InetUtilsProperties properties) {
        return new InetUtils(properties);
    }

}

该类的装配实现中,依赖spring.cloud.util.enabled的配置信息,该配置信息默认true

装配中-EurekaInstanceConfigBean

@Bean
    @ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)
    public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils,
            ManagementMetadataProvider managementMetadataProvider) {

        // 当前实例hostName
        String hostname = getProperty("eureka.instance.hostname");
        
        // prefer-ip-address配置
        boolean preferIpAddress = Boolean
                .parseBoolean(getProperty("eureka.instance.prefer-ip-address"));
        
        // 读取ip-address配置
        String ipAddress = getProperty("eureka.instance.ip-address");

        // 读取secure-port-enabled配置
        boolean isSecurePortEnabled = Boolean
                .parseBoolean(getProperty("eureka.instance.secure-port-enabled"));

        // 获取servlet context-path信息
        String serverContextPath = env.getProperty("server.servlet.context-path", "/");

        // 获取port信息
        int serverPort = Integer.parseInt(
                env.getProperty("server.port", env.getProperty("port", "8080")));

        // 获取management信息
        Integer managementPort = env.getProperty("management.server.port", Integer.class);

        // 获取management servlet context-path信息
        String managementContextPath = env
                .getProperty("management.server.servlet.context-path");

        // 获取jmx 中remote port信息
        Integer jmxPort = env.getProperty("com.sun.management.jmxremote.port",Integer.class);

        // 初始化ConfigBean对象,该对象在初始化过程中, 将会获取当前服务的实例信息
        EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);

        instance.setNonSecurePort(serverPort);
        instance.setInstanceId(getDefaultInstanceId(env));
        instance.setPreferIpAddress(preferIpAddress);
        instance.setSecurePortEnabled(isSecurePortEnabled);
        if (StringUtils.hasText(ipAddress)) {
            instance.setIpAddress(ipAddress);
        }

        if (isSecurePortEnabled) {
            instance.setSecurePort(serverPort);
        }

        if (StringUtils.hasText(hostname)) {
            instance.setHostname(hostname);
        }

        // 获取status-page-url-path信息
        String statusPageUrlPath = getProperty("eureka.instance.status-page-url-path");
        // 获取health-check-url-path信息
        String healthCheckUrlPath = getProperty("eureka.instance.health-check-url-path");

        if (StringUtils.hasText(statusPageUrlPath)) {
            instance.setStatusPageUrlPath(statusPageUrlPath);
        }
        if (StringUtils.hasText(healthCheckUrlPath)) {
            instance.setHealthCheckUrlPath(healthCheckUrlPath);
        }

        // 获取与management 相关的信息组装
        ManagementMetadata metadata = managementMetadataProvider.get(instance, serverPort,
                serverContextPath, managementContextPath, managementPort);

        if (metadata != null) {
            instance.setStatusPageUrl(metadata.getStatusPageUrl());
            instance.setHealthCheckUrl(metadata.getHealthCheckUrl());
            if (instance.isSecurePortEnabled()) {
                instance.setSecureHealthCheckUrl(metadata.getSecureHealthCheckUrl());
            }
            Map<String, String> metadataMap = instance.getMetadataMap();
            metadataMap.computeIfAbsent("management.port",
                    k -> String.valueOf(metadata.getManagementPort()));
        }
        else {
            // without the metadata the status and health check URLs will not be set
            // and the status page and health check url paths will not include the
            // context path so set them here
            if (StringUtils.hasText(managementContextPath)) {
                instance.setHealthCheckUrlPath(
                        managementContextPath + instance.getHealthCheckUrlPath());
                instance.setStatusPageUrlPath(
                        managementContextPath + instance.getStatusPageUrlPath());
            }
        }

        setupJmxPort(instance, jmxPort);
        return instance;
    }

该方法其实是为了组装EurekaInstanceConfigBean 对象信息,该对象信息是通过前置配置eureka.instance.*进行配置。该类型在配置换完成之后,则包含了默认的配置信息。

在初始化EurekaInstanceConfigBean 对象时,传入的参数中需要InetUtil对象作为参数, 我们看下该类主要用处:

public EurekaInstanceConfigBean(InetUtils inetUtils) {
        this.inetUtils = inetUtils;
        this.hostInfo = this.inetUtils.findFirstNonLoopbackHostInfo();
        this.ipAddress = this.hostInfo.getIpAddress();
        this.hostname = this.hostInfo.getHostname();
    }

从代码中不难看出,在初始化bean时,主要需要赋值三个元素:

  • 获取当前服务实例的hostInfo信息
  • 获取当前服务实例的ipAddress信息
  • 获取当前服务实例的hostname信息

这三个信息都是通过inetutils.findFirstNonLoopbackHostInfo()中进行获取,具体查看下该方法中执行逻辑:

public HostInfo findFirstNonLoopbackHostInfo() {
        InetAddress address = findFirstNonLoopbackAddress();
        if (address != null) {
            return convertAddress(address);
        }
        HostInfo hostInfo = new HostInfo();
        hostInfo.setHostname(this.properties.getDefaultHostname());
        hostInfo.setIpAddress(this.properties.getDefaultIpAddress());
        return hostInfo;
    }
public InetAddress findFirstNonLoopbackAddress() {
        InetAddress result = null;
        try {
            int lowest = Integer.MAX_VALUE;

            // 获取当前实例下的所有绑定IP地址列表
            for (Enumeration<NetworkInterface> nics = NetworkInterface
                    .getNetworkInterfaces(); nics.hasMoreElements();) {
                NetworkInterface ifc = nics.nextElement();

                // 判断当前的network interface是否处于UP并且处于running状态
                if (ifc.isUp()) {
                    this.log.trace("Testing interface: " + ifc.getDisplayName());
                    if (ifc.getIndex() < lowest || result == null) {
                        lowest = ifc.getIndex();
                    }
                    else if (result != null) {
                        continue;
                    }

                    // @formatter:off
                    if (!ignoreInterface(ifc.getDisplayName())) {
                        for (Enumeration<InetAddress> addrs = ifc
                                .getInetAddresses(); addrs.hasMoreElements();) {
                            InetAddress address = addrs.nextElement();
                            if (address instanceof Inet4Address
                                    && !address.isLoopbackAddress() // 判断是否为循环IP地址, 该类IP地址主要由软件实现
                                    && isPreferredAddress(address)) {
                                this.log.trace("Found non-loopback interface: "
                                        + ifc.getDisplayName());
                                result = address;
                            }
                        }
                    }
                    // @formatter:on
                }
            }
        }
        catch (IOException ex) {
            this.log.error("Cannot get first non-loopback address", ex);
        }

        if (result != null) {
            return result;
        }

        try {
            // 如果默认没有获取到IP地址,则使用本地地址作为IP地址
            return InetAddress.getLocalHost();
        }
        catch (UnknownHostException e) {
            this.log.warn("Unable to retrieve localhost");
        }

        return null;
    }

装配中-EurekaServiceRegistry

@Bean
    public EurekaServiceRegistry eurekaServiceRegistry() {
        return new EurekaServiceRegistry();
    }

装配中-ApplicationInfoManager

@Bean
        @ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT)
        @org.springframework.cloud.context.config.annotation.RefreshScope
        @Lazy
        public ApplicationInfoManager eurekaApplicationInfoManager(
                EurekaInstanceConfig config) {
            InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
            return new ApplicationInfoManager(config, instanceInfo);
        }

该类主要依赖了前面的EurekaInstanceConfigBean对象,然后通过create方法创建InstanceInfo信息,然后再通过ApplicationInfoManager进行管理当前实例信息.

装配中-EurekaClient

该类作为主要核心类,则主要负责与Eureka Server进行通信,并获取Applications列表, 该类装配实现是在RefreshableEurekaClientConfiguration中实现,因为我们当前处于spring-cloud的环境中,依赖于refresh scope的实现。具体代码如下

@Bean(destroyMethod = "shutdown")
        @ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
        @org.springframework.cloud.context.config.annotation.RefreshScope
        @Lazy
        public EurekaClient eurekaClient(ApplicationInfoManager manager,
                EurekaClientConfig config, EurekaInstanceConfig instance,
                @Autowired(required = false) HealthCheckHandler healthCheckHandler) {
            // If we use the proxy of the ApplicationInfoManager we could run into a
            // problem
            // when shutdown is called on the CloudEurekaClient where the
            // ApplicationInfoManager bean is
            // requested but wont be allowed because we are shutting down. To avoid this
            // we use the
            // object directly.
            ApplicationInfoManager appManager;
            if (AopUtils.isAopProxy(manager)) {
                appManager = ProxyUtils.getTargetObject(manager);
            }
            else {
                appManager = manager;
            }
            CloudEurekaClient cloudEurekaClient = new CloudEurekaClient(appManager,
                    config, this.optionalArgs, this.context);
            cloudEurekaClient.registerHealthCheck(healthCheckHandler);
            return cloudEurekaClient;
        }

在创建EurekaClient对象时,主要使用了CloudEurekaClient对象实现了原始netflix的EurekaClient的实现,同时注册HealthCheckHandler对象。保证EurekaClient正常的运作。

装配中-EurekaRegistration

该类主要保存了当前服务实例的注册信息,具体源码如下:

@Bean
        @org.springframework.cloud.context.config.annotation.RefreshScope
        @ConditionalOnBean(AutoServiceRegistrationProperties.class)
        @ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
        public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient,
                CloudEurekaInstanceConfig instanceConfig,
                ApplicationInfoManager applicationInfoManager,
                @Autowired(required = false) ObjectProvider<HealthCheckHandler> healthCheckHandler) {
            return EurekaRegistration.builder(instanceConfig).with(applicationInfoManager)
                    .with(eurekaClient).with(healthCheckHandler).build();
        }

通过以上信息可以看出, 当前类型生效的有一下前提:

  • AutoServiceRegistrationProperties bean的存在,而该Bean则是通过EnableDiscoveryClient进行初始化
  • 开启spring.cloud.service-registry.auto-registration.enabled配置信息,默认为true

通过以上信息可以看出,在默认情况下,该类是能够自动装配的。

装配中-EurekaAutoServiceRegistration

该类则是开启自动服务注册的入口,配置如下:

@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public EurekaAutoServiceRegistration eurekaAutoServiceRegistration(
        ApplicationContext context, EurekaServiceRegistry registry,
        EurekaRegistration registration) {
    return new EurekaAutoServiceRegistration(context, registry, registration);
}

装配后置-NoopDiscoveryClientAutoConfiguration

该类已经被标记为废除,同时依赖于没有DiscoveryClient对象,通过以上步骤,会发现,当前装配类不会执行。

装配后置-CommonsClientAutoConfiguration

该类主要作为通用的DiscoveryClient的配置, 主要是配置HealIndicator对象,具体代码如下:

@Configuration
@AutoConfigureOrder(0)
public class CommonsClientAutoConfiguration {

    @Configuration
    @EnableConfigurationProperties(DiscoveryClientHealthIndicatorProperties.class)
    @ConditionalOnClass(HealthIndicator.class)
    @ConditionalOnBean(DiscoveryClient.class)
    @ConditionalOnDiscoveryEnabled
    protected static class DiscoveryLoadBalancerConfiguration {

        @Bean
        @ConditionalOnProperty(value = "spring.cloud.discovery.client.health-indicator.enabled", matchIfMissing = true)
        public DiscoveryClientHealthIndicator discoveryClientHealthIndicator(
                ObjectProvider<DiscoveryClient> discoveryClient,
                DiscoveryClientHealthIndicatorProperties properties) {
            return new DiscoveryClientHealthIndicator(discoveryClient, properties);
        }

        @Bean
        @ConditionalOnProperty(value = "spring.cloud.discovery.client.composite-indicator.enabled", matchIfMissing = true)
        @ConditionalOnBean({ DiscoveryHealthIndicator.class, HealthAggregator.class })
        public DiscoveryCompositeHealthIndicator discoveryCompositeHealthIndicator(
                HealthAggregator aggregator, List<DiscoveryHealthIndicator> indicators) {
            return new DiscoveryCompositeHealthIndicator(aggregator, indicators);
        }

        @Bean
        public HasFeatures commonsFeatures() {
            return HasFeatures.abstractFeatures(DiscoveryClient.class,
                    LoadBalancerClient.class);
        }

    }

    @Configuration
    @ConditionalOnClass(Endpoint.class)
    @ConditionalOnProperty(value = "spring.cloud.features.enabled", matchIfMissing = true)
    protected static class ActuatorConfiguration {

        @Autowired(required = false)
        private List<HasFeatures> hasFeatures = new ArrayList<>();

        @Bean
        @ConditionalOnEnabledEndpoint
        public FeaturesEndpoint featuresEndpoint() {
            return new FeaturesEndpoint(this.hasFeatures);
        }

    }

}

 

装配后置-ServiceRegistryAutoConfiguration

该类主要是对ServiceRegistry的再次封装,返回ServiceRegistryEndpoint对象,具体配置如下:

@Configuration
public class ServiceRegistryAutoConfiguration {

    @ConditionalOnBean(ServiceRegistry.class)
    @ConditionalOnClass(Endpoint.class)
    protected class ServiceRegistryEndpointConfiguration {

        @Autowired(required = false)
        private Registration registration;

        @Bean
        @ConditionalOnEnabledEndpoint
        public ServiceRegistryEndpoint serviceRegistryEndpoint(
                ServiceRegistry serviceRegistry) {
            ServiceRegistryEndpoint endpoint = new ServiceRegistryEndpoint(
                    serviceRegistry);
            endpoint.setRegistration(this.registration);
            return endpoint;
        }

    }

}

以上就是Eureka Client整体装配过程,后面将对里面的细节进行详细的说明。

当我们有时候会发现会后的两个配置不会执行,是因为最后两项都是对registry的服务状态的暴露,因此需要我们引入spring-boot-starter-actuator之后,就能够正常执行了

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注