SpringCloud源码剖析-Eureka Client 初始化过程

简介: 在spring-cloud-commons-2.0.1.RELEASE.jar SpringCloud公共包中/META-INF/spring.factories配置文件中有AutoServiceRegistrationAutoConfiguration自动配置类,当应用启动SpringBoot会扫描到该自动配置类,然后注册到Spring容器

前言

前面我们简单了解了一下SpringCloud Eureka的核心API,这一章我们跟踪一下Eureka的启动过程

1.EurekaClientAutoConfiguration:Eureka自动配置

有了解过SpringBoot自动配置的同学应该知道,在主程序启动类上我们会贴一个 @SpringBootApplication 注解,该注解里面包含了一个 @EnableAutoConfiguration 注解,该注解的作用是开启SpringBoot的自动配置,即:在 @EnableAutoConfiguration 标签中使用了一个 AutoConfigurationImportSelector选择器,该选择器会去扫描当前classpath环境中的 META-INF/spring.factories 文件中的自动配置类,然后完成相关的自动配置。如下:

  • @SpringBootApplication标签源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters= {
@Filter(type=FilterType.CUSTOM, classes=TypeExcludeFilter.class),
@Filter(type=FilterType.CUSTOM, classes=AutoConfigurationExcludeFilter.class) })
public@interfaceSpringBootApplication {}
  • @EnableAutoConfiguration 标签源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)
public@interfaceEnableAutoConfiguration {...}
  • AutoConfigurationImportSelector选择器源码
publicclassAutoConfigurationImportSelectorimplementsDeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
//省略部分代码......//扫描classpath下的spring.factories自动配置@OverridepublicString[] selectImports(AnnotationMetadataannotationMetadata) {
if (!isEnabled(annotationMetadata)) {
returnNO_IMPORTS;
        }
AutoConfigurationMetadataautoConfigurationMetadata=AutoConfigurationMetadataLoader                .loadMetadata(this.beanClassLoader);
AnnotationAttributesattributes=getAttributes(annotationMetadata);
List<String>configurations=getCandidateConfigurations(annotationMetadata,
attributes);
configurations=removeDuplicates(configurations);
Set<String>exclusions=getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations=filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
returnStringUtils.toStringArray(configurations);
    }
}

selectImports 方法中会将扫描到的 META-INF/spring.factories 文件中的自动配置的一些类加载进来,然后注册到Spring容器中

其中在 spring-cloud-netflix-eureka-client-2.0.1.RELEASE.jar 这个jar包里面就有一个 META-INF/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,\org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration

我们这里需要重点关注是 EurekaClientAutoConfiguration 这个类,从名字就能看出它是对EureakClient的自动配置,来详细看一下它的源码

//EurekaClient自动配置@Configuration@EnableConfigurationProperties@ConditionalOnClass(EurekaClientConfig.class)
@Import(DiscoveryClientOptionalArgsConfiguration.class)
@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)
@ConditionalOnProperty(value="eureka.client.enabled", matchIfMissing=true)
@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"})
publicclassEurekaClientAutoConfiguration {
...省略...
//注册EurekaClientConfigBean,该类实现了EurekaClientConfig客户端配置接口,EurekaClientConfigBean封装了客户端实例Eureaka Client注册到Eureak Server 服务端所需要的注册信息,比如:服务注册地址,拉取服务注册表的时间间隔, 注册信息复制到EurekaServer的时间等等@Bean@ConditionalOnMissingBean(value=EurekaClientConfig.class, search=SearchStrategy.CURRENT)
publicEurekaClientConfigBeaneurekaClientConfigBean(ConfigurableEnvironmentenv) {
EurekaClientConfigBeanclient=newEurekaClientConfigBean();
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);
        }
returnclient;
    }
//注册EurekaInstanceConfigBean , eureka实例配置,实现了EurekaInstanceConfig配置接口,其中包括了:获取实例ID,//获取https协议端口,获取应用名,获取应用组名,获取租约心跳间隔时间,租约到期时间,获取ip,获取主机名等等方法。@Bean@ConditionalOnMissingBean(value=EurekaInstanceConfig.class, search=SearchStrategy.CURRENT)
publicEurekaInstanceConfigBeaneurekaInstanceConfigBean(InetUtilsinetUtils,
ManagementMetadataProvidermanagementMetadataProvider) {
Stringhostname=getProperty("eureka.instance.hostname");
booleanpreferIpAddress=Boolean.parseBoolean(getProperty("eureka.instance.prefer-ip-address"));
StringipAddress=getProperty("eureka.instance.ip-address");
booleanisSecurePortEnabled=Boolean.parseBoolean(getProperty("eureka.instance.secure-port-enabled"));
StringserverContextPath=env.getProperty("server.context-path", "/");
intserverPort=Integer.valueOf(env.getProperty("server.port", env.getProperty("port", "8080")));
IntegermanagementPort=env.getProperty("management.server.port", Integer.class);// nullable. should be wrapped into optionalStringmanagementContextPath=env.getProperty("management.server.servlet.context-path");// nullable. should be wrapped into optionalIntegerjmxPort=env.getProperty("com.sun.management.jmxremote.port", Integer.class);//nullableEurekaInstanceConfigBeaninstance=newEurekaInstanceConfigBean(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);
        }
StringstatusPageUrlPath=getProperty("eureka.instance.status-page-url-path");
StringhealthCheckUrlPath=getProperty("eureka.instance.health-check-url-path");
if (StringUtils.hasText(statusPageUrlPath)) {
instance.setStatusPageUrlPath(statusPageUrlPath);
        }
if (StringUtils.hasText(healthCheckUrlPath)) {
instance.setHealthCheckUrlPath(healthCheckUrlPath);
        }
ManagementMetadatametadata=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();
if (metadataMap.get("management.port") ==null) {
metadataMap.put("management.port", 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 hereif(StringUtils.hasText(managementContextPath)) {
instance.setHealthCheckUrlPath(managementContextPath+instance.getHealthCheckUrlPath());
instance.setStatusPageUrlPath(managementContextPath+instance.getStatusPageUrlPath());
            }
        }
setupJmxPort(instance, jmxPort);
returninstance;
    }
//注册DiscoveryClient服务发现客户端,它是spring-cloud-commons:2.0.1.RELEASE SpringCloud公共抽象包中的(org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient),//是Spring Cloudd的服务发现抽象接口 EurekaDiscoveryClient是它的实现@BeanpublicDiscoveryClientdiscoveryClient(EurekaInstanceConfigconfig, EurekaClientclient) {
returnnewEurekaDiscoveryClient(config, client);
    }
//Eureak服务注册器,他是SpringCloud 在Netflix标准上拓展出来的,提供了服务注册,取消注册,设置服务状态,获取服务状态方法,ServiceRegistry负责服务注册,DiscoveryClient负责服务发现@BeanpublicEurekaServiceRegistryeurekaServiceRegistry() {
returnnewEurekaServiceRegistry();
    }
//EurekaAutoServiceRegistration :eureka服务自动注册器@Bean@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value="spring.cloud.service-registry.auto-registration.enabled", matchIfMissing=true)
publicEurekaAutoServiceRegistrationeurekaAutoServiceRegistration(ApplicationContextcontext, EurekaServiceRegistryregistry,
EurekaRegistrationregistration) {
returnnewEurekaAutoServiceRegistration(context, registry, registration);
    }
//取消注册@Overridepublicvoidstop() {
this.serviceRegistry.deregister(this.registration);
this.running.set(false);
    }
//EureakClient配置,该配置没有刷新功能@Configuration@ConditionalOnMissingRefreshScope//不可刷新protectedstaticclassEurekaClientConfiguration {
@AutowiredprivateApplicationContextcontext;
@AutowiredprivateAbstractDiscoveryClientOptionalArgs<?>optionalArgs;
//注册EurekaClient  客户端对象 , 它有一个实现 DiscoveryClient ,//这个实现 DiscoveryClient  是eureka-client:1.9.3包里面的,是Euraek的服务发现的核心实现@Bean(destroyMethod="shutdown")
@ConditionalOnMissingBean(value=EurekaClient.class, search=SearchStrategy.CURRENT)
publicEurekaClienteurekaClient(ApplicationInfoManagermanager, EurekaClientConfigconfig) {
returnnewCloudEurekaClient(manager, config, this.optionalArgs,
this.context);
        }
//注册ApplicationInfoManager  ,该对象用来管理 InstanceInfo@Bean@ConditionalOnMissingBean(value=ApplicationInfoManager.class, search=SearchStrategy.CURRENT)
publicApplicationInfoManagereurekaApplicationInfoManager(
EurekaInstanceConfigconfig) {
InstanceInfoinstanceInfo=newInstanceInfoFactory().create(config);
returnnewApplicationInfoManager(config, instanceInfo);
        }
//注册EurekaRegistration,它是Eureka实例的服务注册信息,开启自动注册时,该对象才会被注册@Bean@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value="spring.cloud.service-registry.auto-registration.enabled", matchIfMissing=true)
publicEurekaRegistrationeurekaRegistration(EurekaClienteurekaClient,
CloudEurekaInstanceConfiginstanceConfig,
ApplicationInfoManagerapplicationInfoManager,
@Autowired(required=false) ObjectProvider<HealthCheckHandler>healthCheckHandler) {
returnEurekaRegistration.builder(instanceConfig)
                    .with(applicationInfoManager)
                    .with(eurekaClient)
                    .with(healthCheckHandler)
                    .build();
        }
    }
//这里也是EurekaClient的配置,只是可刷新的@Configuration@ConditionalOnRefreshScopeprotectedstaticclassRefreshableEurekaClientConfiguration {
@AutowiredprivateApplicationContextcontext;
@AutowiredprivateAbstractDiscoveryClientOptionalArgs<?>optionalArgs;
//注册Eureak客户端EurekaClient, @Lazy 懒初始化,该bean被用到的时候会被创建//EurekaClient有一个实现 DiscoveryClient这个实现 是eureka-client:1.9.3包里面的,是Euraek的服务发现的核心实现@Bean(destroyMethod="shutdown")
@ConditionalOnMissingBean(value=EurekaClient.class, search=SearchStrategy.CURRENT)
@org.springframework.cloud.context.config.annotation.RefreshScope@LazypublicEurekaClienteurekaClient(ApplicationInfoManagermanager, EurekaClientConfigconfig, EurekaInstanceConfiginstance) {
manager.getInfo(); // force initializationreturnnewCloudEurekaClient(manager, config, this.optionalArgs,
this.context);
        }
//注册ApplicationInfoManager,它是InstanceInfo的管理器@Bean@ConditionalOnMissingBean(value=ApplicationInfoManager.class, search=SearchStrategy.CURRENT)
@org.springframework.cloud.context.config.annotation.RefreshScope@LazypublicApplicationInfoManagereurekaApplicationInfoManager(EurekaInstanceConfigconfig) {
//注意:这里的InstanceInfo被创建的状态是InstanceStatus.STARTING;后面在初始化的过程中会修改为 InstanceStatus.UP;InstanceInfoinstanceInfo=newInstanceInfoFactory().create(config);
returnnewApplicationInfoManager(config, instanceInfo);
        }
@Bean@org.springframework.cloud.context.config.annotation.RefreshScope@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value="spring.cloud.service-registry.auto-registration.enabled", matchIfMissing=true)
publicEurekaRegistrationeurekaRegistration(EurekaClienteurekaClient,
CloudEurekaInstanceConfiginstanceConfig,
ApplicationInfoManagerapplicationInfoManager,
@Autowired(required=false) ObjectProvider<HealthCheckHandler>healthCheckHandler) {
returnEurekaRegistration.builder(instanceConfig)
                    .with(applicationInfoManager)
                    .with(eurekaClient)
                    .with(healthCheckHandler)
                    .build();
        }
    }
    ...省略...
//健康指示器,用来管理服务的健康状况@Configuration@ConditionalOnClass(Health.class)
protectedstaticclassEurekaHealthIndicatorConfiguration {
@Bean@ConditionalOnMissingBean@ConditionalOnEnabledHealthIndicator("eureka")
publicEurekaHealthIndicatoreurekaHealthIndicator(EurekaClienteurekaClient,
EurekaInstanceConfiginstanceConfig, EurekaClientConfigclientConfig) {
returnnewEurekaHealthIndicator(eurekaClient, instanceConfig, clientConfig);
        }
    }

总结一下:这里注册了很多对象比较重要的有:

1.EurekaClientConfigBean :这个类实现了netflix 的 EurekaClientConfig客户端配置接口,是对Eureka客户端的配置对象,即我们在配置文件中配置的 eureka.client 节点下的配置都会被封装到该对象中。

2.EurekaInstanceConfigBean: 该类实现了netflix的EurekaInstanceConfig接口,是的服务实例信息配置,ApplicationManager通过该接口用来构建InstanceConfig,比如我们在配置文件中配置的eureka.instance 开头的配置就会配置到该对象中。

3.DiscoveryClient:注册了DiscoveryClient服务发现客户端接口,默认实现是EurekaDiscoveryClient。这个类来源于spring-cloud-common.2.0.1.jar 这个包,这个包是springcloud的抽象包的公共包里面制定了一系列公共的API,而DiscoveryClient中包含了服务发现的基本公共方法

4.配置了一些客户端自动注册组件

  • EurekaServiceRegistry: Eureak服务注册器,他是SpringCloud 在Netflix标准上拓展出来的,提供了服务注册,取消注册, 关闭服务,设置服务状态,获取服务状态方法
  • EurekaRegistration: 它是Eureka实例的服务注册信息对象,开启自动注册时,该对象才会被注册,EurekaServiceRegistry负责服务注册,EurekaRegistration存放服务注册信息
  • EurekaAutoServiceRegistration: Eureka服务自动注册器,用来注册EurekaRegistration信息

5.EurekaClient:这里通过CloudEurekaClient来实例化,他是用来和EureakServer进行交互,是netflix提供的接口类,它还有个比较重要的实现 DiscoveryClient 主要负责服务发现,这个是与注册中心EureakServer交互的最核心的实现,包括服务注册,注册表拉取,服务续约等等功能都在里面提现。需要注意的是该Bean在注册的时候使用了@Lazy进行延迟创建

6.ApplicationInfoManager:这个类是用来管理实例注册信息InstanceInfo的,其中还包括了对实例状的态监听机制

7.EurekaHealthIndicator:这个是用作服务的健康检查的,以及提供了获取服务状态,以及获取Applications服务注册表等方法

2.EurekaAutoServiceRegistration:Eureaka自动注册

spring-cloud-commons-2.0.1.RELEASE.jar SpringCloud公共包中/META-INF/spring.factories配置文件中有AutoServiceRegistrationAutoConfiguration自动配置类,当应用启动SpringBoot会扫描到该自动配置类,然后注册到Spring容器

#AutoConfigurationorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,\org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration

ServiceRegistryAutoConfiguration是配置注册的服务的端点信息,可通过端点 “/service-registry”查看到服务注册情况

@ConfigurationpublicclassServiceRegistryAutoConfiguration {
@ConditionalOnBean(ServiceRegistry.class)
@ConditionalOnClass(Endpoint.class)
protectedclassServiceRegistryEndpointConfiguration {
@Autowired(required=false)
privateRegistrationregistration;
@Bean@ConditionalOnEnabledEndpointpublicServiceRegistryEndpointserviceRegistryEndpoint(ServiceRegistryserviceRegistry) {
ServiceRegistryEndpointendpoint=newServiceRegistryEndpoint(serviceRegistry);
endpoint.setRegistration(registration);
returnendpoint;
        }
    }
}
------------------------------------------------------------------------//查看注册服务端点@Endpoint(id="service-registry")
publicclassServiceRegistryEndpoint {
privatefinalServiceRegistryserviceRegistry;
privateRegistrationregistration;
publicServiceRegistryEndpoint(ServiceRegistry<?>serviceRegistry) {
this.serviceRegistry=serviceRegistry;
    }
publicvoidsetRegistration(Registrationregistration) {
this.registration=registration;
    }
@WriteOperationpublicResponseEntity<?>setStatus(Stringstatus) {
Assert.notNull(status, "status may not by null");
if (this.registration==null) {
returnResponseEntity.status(HttpStatus.NOT_FOUND).body("no registration found");
        }
this.serviceRegistry.setStatus(this.registration, status);
returnResponseEntity.ok().build();
    }
//获取服务状态@ReadOperationpublicResponseEntitygetStatus() {
if (this.registration==null) {
returnResponseEntity.status(HttpStatus.NOT_FOUND).body("no registration found");
        }
returnResponseEntity.ok().body(this.serviceRegistry.getStatus(this.registration));
    }
}

AutoServiceRegistrationAutoConfiguration是开启服务自动注册其中注入了AutoServiceRegistration确保该类会被注册,源码如下

@Configuration@Import(AutoServiceRegistrationConfiguration.class)
@ConditionalOnProperty(value="spring.cloud.service-registry.auto-registration.enabled", matchIfMissing=true)
publicclassAutoServiceRegistrationAutoConfiguration {
@Autowired(required=false)
privateAutoServiceRegistrationautoServiceRegistration;
@AutowiredprivateAutoServiceRegistrationPropertiesproperties;
@PostConstructprotectedvoidinit() {
if (autoServiceRegistration==null&&this.properties.isFailFast()) {
thrownewIllegalStateException("Auto Service Registration has been requested, but there is no AutoServiceRegistration bean");
        }
    }
}

EurekaAutoServiceRegistration是Eureka的自动注册器,在EurekaClientAutoConfiguration中对EurekaAutoServiceRegistration做了Bean的定义,当开启spring.cloud.service-registry.auto-registration.enabled=true(默认)该Bean注册起效 , 它是 AutoServiceRegistration的实现

EurekaAutoServiceRegistration 源码如下:

//自动注册器publicclassEurekaAutoServiceRegistrationimplementsAutoServiceRegistration, SmartLifecycle, Ordered {
privatestaticfinalLoglog=LogFactory.getLog(EurekaAutoServiceRegistration.class);
privateAtomicBooleanrunning=newAtomicBoolean(false);
privateintorder=0;
privateAtomicIntegerport=newAtomicInteger(0);
privateApplicationContextcontext;
//服务注册器privateEurekaServiceRegistryserviceRegistry;
//服务注册信息登记privateEurekaRegistrationregistration;
publicEurekaAutoServiceRegistration(ApplicationContextcontext, EurekaServiceRegistryserviceRegistry, EurekaRegistrationregistration) {
this.context=context;
this.serviceRegistry=serviceRegistry;
this.registration=registration;
    }
//开启服务注册@Overridepublicvoidstart() {
// only set the port if the nonSecurePort or securePort is 0 and this.port != 0if (this.port.get() !=0) {
if (this.registration.getNonSecurePort() ==0) {
this.registration.setNonSecurePort(this.port.get());
            }
if (this.registration.getSecurePort() ==0&&this.registration.isSecure()) {
this.registration.setSecurePort(this.port.get());
            }
        }
// only initialize if nonSecurePort is greater than 0 and it isn't already running// because of containerPortInitializer belowif (!this.running.get() &&this.registration.getNonSecurePort() >0) {
//调用服务注册器进行服务注册this.serviceRegistry.register(this.registration);
//发布一个服务注册事件this.context.publishEvent(
newInstanceRegisteredEvent<>(this, this.registration.getInstanceConfig()));
this.running.set(true);
        }
    }
@Overridepublicvoidstop() {
//服务停止,取消服务注册this.serviceRegistry.deregister(this.registration);
this.running.set(false);
    }
@OverridepublicbooleanisRunning() {
//获取服务状态是否运行returnthis.running.get();
    }
@OverridepublicintgetPhase() {
return0;
    }
@OverridepublicbooleanisAutoStartup() {
returntrue;
    }
//停止服务@Overridepublicvoidstop(Runnablecallback) {
stop();
callback.run();
    }
@OverridepublicintgetOrder() {
returnthis.order;
    }
//监听web启动完成事件,执行start()方法开启服务注册@EventListener(WebServerInitializedEvent.class)
publicvoidonApplicationEvent(WebServerInitializedEventevent) {
// TODO: take SSL into accountintlocalPort=event.getWebServer().getPort();
if (this.port.get() ==0) {
log.info("Updating port to "+localPort);
this.port.compareAndSet(0, localPort);
start();
        }
    }
//监听应用关闭事件,执行stop()方法@EventListener(ContextClosedEvent.class)
publicvoidonApplicationEvent(ContextClosedEventevent) {
if( event.getApplicationContext() ==context ) {
stop();
        }
    }
}

EurekaAutoServiceRegistration监听了应用启动事件,调用EurekaServiceRegistry.register进行服务注册,同时监听应用关闭事件,调用EurekaServiceRegistry.deregister取消服务注册

3.EurekaServiceRegistry:服务注册器

EurekaServiceRegistry是Eureak服务注册器,他是SpringCloud 在Netflix标准ServiceRegistry上拓展出来的,提供了服务注册,取消注册, 关闭服务,设置服务状态,获取服务状态方法

ServiceRegistryEureakaServiceRegistry的接口,他是Netflix的服务发现抽象,ServiceRegistry需要依赖EurekaRegistration,EurekaRegistration是ServiceInstance的实现它约定了服务发现的实例应用的通用的信息

在应用启动时,EurekaAutoServiceRegistration会监听应用启动完成事件,在start()方法中调用,EureakaServiceRegistry.register方法开始服务注册,源码如下

//服务注册publicclassEurekaServiceRegistryimplementsServiceRegistry<EurekaRegistration> {
privatestaticfinalLoglog=LogFactory.getLog(EurekaServiceRegistry.class);
//注册方法,注册EurekaRegistration@Overridepublicvoidregister(EurekaRegistrationreg) {
//初始化客户端,该方法会触发 EurekaClient 的创建maybeInitializeClient(reg);
if (log.isInfoEnabled()) {
log.info("Registering application "+reg.getInstanceConfig().getAppname()
+" with eureka with status "+reg.getInstanceConfig().getInitialStatus());
        }
//初始化客户端状态,把InstanceStatus设置为 InstanceStatus.UP;reg.getApplicationInfoManager()
                .setInstanceStatus(reg.getInstanceConfig().getInitialStatus());
//注册健康检查处理器reg.getHealthCheckHandler().ifAvailable(healthCheckHandler->reg.getEurekaClient().registerHealthCheck(healthCheckHandler));
    }
//初始化客户端,reg.getEurekaClient()会触发EureakClient(DiscoveryClient)的初始化privatevoidmaybeInitializeClient(EurekaRegistrationreg) {
// force initialization of possibly scoped proxiesreg.getApplicationInfoManager().getInfo();
//初始化Eurak客户端reg.getEurekaClient().getApplications();
    }
//取消注册,设置服务状态为down@Overridepublicvoidderegister(EurekaRegistrationreg) {
if (reg.getApplicationInfoManager().getInfo() !=null) {
if (log.isInfoEnabled()) {
log.info("Unregistering application "+reg.getInstanceConfig().getAppname()
+" with eureka with status DOWN");
            }
reg.getApplicationInfoManager().setInstanceStatus(InstanceInfo.InstanceStatus.DOWN);
//shutdown of eureka client should happen with EurekaRegistration.close()//auto registration will create a bean which will be properly disposed//manual registrations will need to call close()        }
    }
//设置服务注册状态@OverridepublicvoidsetStatus(EurekaRegistrationregistration, Stringstatus) {
InstanceInfoinfo=registration.getApplicationInfoManager().getInfo();
//TODO: howto deal with delete properly?if ("CANCEL_OVERRIDE".equalsIgnoreCase(status)) {
registration.getEurekaClient().cancelOverrideStatus(info);
return;
        }
//TODO: howto deal with status types across discovery systems?InstanceInfo.InstanceStatusnewStatus=InstanceInfo.InstanceStatus.toEnum(status);
registration.getEurekaClient().setStatus(newStatus, info);
    }
//获取服务注册状态@OverridepublicObjectgetStatus(EurekaRegistrationregistration) {
Stringappname=registration.getInstanceConfig().getAppname();
StringinstanceId=registration.getInstanceConfig().getInstanceId();
InstanceInfoinfo=registration.getEurekaClient().getInstanceInfo(appname, instanceId);
HashMap<String, Object>status=newHashMap<>();
if (info!=null) {
status.put("status", info.getStatus().toString());
status.put("overriddenStatus", info.getOverriddenStatus().toString());
        } else {
status.put("status", UNKNOWN.toString());
        }
returnstatus;
    }
publicvoidclose() {
    }
}

EurekaServiceRegistry中register方法初始化了Eureka客户端,以及注册了健康检查处理器,在private void maybeInitializeClient(EurekaRegistration reg)方法中reg.getEurekaClient().getApplications();会触发在EurekaClientAutoConfiguration自动配置中对EureakClient的延迟创建,使用的EurekaClient的实现com.netflix.discovery.DiscoveryClient

4.DiscoveryClient:服务发现

这个类来源于 eureka-client-19.3.jar这个包 ,DiscoveryClient这个类是用来和Eureak Server服务端做交互,通过程序的主入口类上的@EnableDiscoveryClient标签开启,该类在EurekaClientAutoConfiguration中被注册,该类的继承关系如下:

CloudEurekaClient实现了 com.netflix.discovery.DiscoveryClient接口,DiscoveryClient实现了com.netflix.discovery.EurekaClient接口,

EurekaClient是Netflix提供的服务发现抽象的接口,DiscoveryClient是其默认实现,CloudEurekaClient是spring cloud的提供的,其中重写了onCacheRefreshed()方法,目的是在刷新本地注册表缓存之后用于使用ApplicationEventPublisher发布HeartbeatEvent事件。

com.netflix.discovery.shared.LookupService

服务发现顶级接口,里面包含了服务查找方法

/*** Lookup service for finding active instances.* 用来查找活动的服务实例*/publicinterfaceLookupService<T> {
/**根据应用名获取该名字对应的应用,Application中描述了该appName对应的多个应用实例*/ApplicationgetApplication(StringappName);
/**获取所有的应用注册列表,Applications是eureka server返回的所有注册信息*/ApplicationsgetApplications();
/**根据ID获取服务实例信息信息对象列表*/List<InstanceInfo>getInstancesById(Stringid);
InstanceInfogetNextServerFromEureka(StringvirtualHostname, booleansecure);
}

com.netflix.discovery.EurekaClient

这里接口也来源于com.netflix.discovery.EurekaClient.1.9.3.jar这个包,EurekaClient是DiscoverClient的接口,定义了服务发现的基本功能,如下:

  • 提供获取InstanceInfo 服务实例信息的功能(以不同的方式)
  • 提供获取本地服务数据(已知地区、自有AZ等)
  • 提供注册和访问客户端的healthcheck健康检查处理程序的能力

他的源码如下

/**为当前服务实现DiscoveryClient定义一个简单的接口* Define a simple interface over the current DiscoveryClient implementation.** This interface does NOT try to clean up the current client interface for eureka 1.x. Rather it tries* to provide an easier transition path from eureka 1.x to eureka 2.x.EurekaClient API合同包括:- 提供获取InstanceInfo 服务实例信息的功能(以不同的方式)- 提供获取本地客户数据的能力(已知地区、自有AZ等)- 提供注册和访问客户端的healthcheck处理程序的能力* EurekaClient API contracts are:*  - provide the ability to get InstanceInfo(s) (in various different ways)*  - provide the ability to get data about the local Client (known regions, own AZ etc)*  - provide the ability to register and access the healthcheck handler for the client** @author David Liu*/@ImplementedBy(DiscoveryClient.class)   //实现于DiscoveryClientpublicinterfaceEurekaClientextendsLookupService {
// ========================// getters for InstanceInfo// ========================/**根据region地区获取服务列表Applications*/publicApplicationsgetApplicationsForARegion(@NullableStringregion);
/**根据服务注册地址,返回Applications*/publicApplicationsgetApplications(StringserviceUrl);
/**根据地址获取服务实例信息列表*/publicList<InstanceInfo>getInstancesByVipAddress(StringvipAddress, booleansecure);
/** 获取服务的远程注册状态 :InstanceStatus是用来描述服务注册状态的,状态有:UP,DOWN,STARTING,OUT_OF_SERVICE,UNKNOWN*/publicInstanceInfo.InstanceStatusgetInstanceRemoteStatus();
    ...省略一些方法..
/**注册健康检查回调处理器*/@DeprecatedpublicvoidregisterHealthCheckCallback(HealthCheckCallbackcallback);
/**注册健康检查处理器*/publicvoidregisterHealthCheck(HealthCheckHandlerhealthCheckHandler);
    ...省略...
//注册eureak监听器,监听eureka内部状态变化publicvoidregisterEventListener(EurekaEventListenereventListener);
/**Eureak客户端down机,从Eureak服务端取消服务注册*/publicvoidshutdown();
    ...省略...

该接口是Eureak服务发现接口,在LookupService的基础上扩展,主要定义方法有:

  • 获取Applications服务列表
  • 获取InstanceInfo服务实例信息
  • 获取服务的远程状态(UP,DOWN,STARTING,OUT_OF_SERVICE,UNKNOWN)
  • 注册健康检查回调处理器
  • 注册健康检查处理器
  • 注册eureka状态监听器,监听eureka的状态变化
  • Eureak客户端down机,从Eureak服务端取消服务注册

com.netflix.discovery.DiscoveryClient

服务发现核心实现与Eureka Server交互,功能包括:

  • Eureka客户端负责向Eureka服务器注册实例
  • 与Eureak服务器续租
  • 关闭期间,从Eureka服务器取消租约
  • 查询在Eureka服务器上注册的服务/实例列表

5.总结

这里总结一个Eureak的启动流程

  1. 当程序启动,SpringBoot开启自动配置,EurekaClientAutoConfiguration Eureka客户端自动配置,和EurekaAutoServiceRegistration Eureka自动注册启动
  2. EurekaClientAutoConfiguration进行一些列组件的注册,包括对DiscoverClient服务发现客户端的组合,对EurekaServiceRegistry服务注册器的注册
  3. EurekaAutoServiceRegistration监听Web程序启动,调用EurekaServiceRegistry进行服务注册
  4. EurekaServiceRegistry是服务注册器,提供了服务注册,取消注册等方法,其中对DiscoverClient进行初始化,并注册健康检查处理器
  5. 5.DiscoveryClient服务发现客户端,负责与Eureka服务端进行交互


目录
相关文章
|
2月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
405 37
|
3月前
|
人工智能 前端开发 Java
【实操】Spring Cloud Alibaba AI,阿里AI这不得玩一下(含前后端源码)
本文介绍了如何使用 **Spring Cloud Alibaba AI** 构建基于 Spring Boot 和 uni-app 的聊天机器人应用。主要内容包括:Spring Cloud Alibaba AI 的概念与功能,使用前的准备工作(如 JDK 17+、Spring Boot 3.0+ 及通义 API-KEY),详细实操步骤(涵盖前后端开发工具、组件选择、功能分析及关键代码示例)。最终展示了如何成功实现具备基本聊天功能的 AI 应用,帮助读者快速搭建智能聊天系统并探索更多高级功能。
1331 2
【实操】Spring Cloud Alibaba AI,阿里AI这不得玩一下(含前后端源码)
|
1月前
|
负载均衡 算法 Nacos
SpringCloud 微服务nacos和eureka
SpringCloud 微服务nacos和eureka
59 0
|
1月前
|
负载均衡 Java API
【Spring Cloud生态】Spring Cloud Gateway基本配置
【Spring Cloud生态】Spring Cloud Gateway基本配置
37 0
|
2月前
|
负载均衡 Java Nacos
SpringCloud基础1——远程调用、Eureka,Nacos注册中心、Ribbon负载均衡
微服务介绍、SpringCloud、服务拆分和远程调用、Eureka注册中心、Ribbon负载均衡、Nacos注册中心
SpringCloud基础1——远程调用、Eureka,Nacos注册中心、Ribbon负载均衡
|
3月前
|
Java Spring
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
|
3月前
|
Java Spring 容器
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
|
3月前
|
存储 Java Spring
【Azure Spring Cloud】Azure Spring Cloud服务,如何获取应用程序日志文件呢?
【Azure Spring Cloud】Azure Spring Cloud服务,如何获取应用程序日志文件呢?
|
3月前
|
SQL Java 数据库连接
【Azure Spring Cloud】Azure Spring Cloud connect to SQL using MSI
【Azure Spring Cloud】Azure Spring Cloud connect to SQL using MSI
|
3月前
|
Java 开发工具 Spring
【Azure Spring Cloud】使用azure-spring-boot-starter-storage来上传文件报错: java.net.UnknownHostException: xxxxxxxx.blob.core.windows.net: Name or service not known
【Azure Spring Cloud】使用azure-spring-boot-starter-storage来上传文件报错: java.net.UnknownHostException: xxxxxxxx.blob.core.windows.net: Name or service not known