前言
前面我们简单了解了一下SpringCloud Eureka的核心API,这一章我们跟踪一下Eureka的启动过程
1.EurekaClientAutoConfiguration:Eureka自动配置
有了解过SpringBoot自动配置的同学应该知道,在主程序启动类上我们会贴一个 @SpringBootApplication
注解,该注解里面包含了一个 @EnableAutoConfiguration
注解,该注解的作用是开启SpringBoot的自动配置,即:在 @EnableAutoConfiguration
标签中使用了一个 AutoConfigurationImportSelector
选择器,该选择器会去扫描当前classpath环境中的 META-INF/spring.factories 文件中的自动配置类,然后完成相关的自动配置。如下:
- @SpringBootApplication标签源码
ElementType.TYPE) (RetentionPolicy.RUNTIME) (excludeFilters= { (type=FilterType.CUSTOM, classes=TypeExcludeFilter.class), (type=FilterType.CUSTOM, classes=AutoConfigurationExcludeFilter.class) }) (public@interfaceSpringBootApplication {}
- @EnableAutoConfiguration 标签源码
ElementType.TYPE) (RetentionPolicy.RUNTIME) (AutoConfigurationImportSelector.class) (public@interfaceEnableAutoConfiguration {...}
- AutoConfigurationImportSelector选择器源码
publicclassAutoConfigurationImportSelectorimplementsDeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { //省略部分代码......//扫描classpath下的spring.factories自动配置publicString[] 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自动配置EurekaClientConfig.class) (DiscoveryClientOptionalArgsConfiguration.class) (EurekaDiscoveryClientConfiguration.Marker.class) (value="eureka.client.enabled", matchIfMissing=true) (NoopDiscoveryClientAutoConfiguration.class, ({ CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class }) 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的时间等等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,获取主机名等等方法。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是它的实现publicDiscoveryClientdiscoveryClient(EurekaInstanceConfigconfig, EurekaClientclient) { returnnewEurekaDiscoveryClient(config, client); } //Eureak服务注册器,他是SpringCloud 在Netflix标准上拓展出来的,提供了服务注册,取消注册,设置服务状态,获取服务状态方法,ServiceRegistry负责服务注册,DiscoveryClient负责服务发现publicEurekaServiceRegistryeurekaServiceRegistry() { returnnewEurekaServiceRegistry(); } //EurekaAutoServiceRegistration :eureka服务自动注册器AutoServiceRegistrationProperties.class) (value="spring.cloud.service-registry.auto-registration.enabled", matchIfMissing=true) (publicEurekaAutoServiceRegistrationeurekaAutoServiceRegistration(ApplicationContextcontext, EurekaServiceRegistryregistry, EurekaRegistrationregistration) { returnnewEurekaAutoServiceRegistration(context, registry, registration); } //取消注册publicvoidstop() { this.serviceRegistry.deregister(this.registration); this.running.set(false); } //EureakClient配置,该配置没有刷新功能//不可刷新protectedstaticclassEurekaClientConfiguration { privateApplicationContextcontext; privateAbstractDiscoveryClientOptionalArgs<?>optionalArgs; //注册EurekaClient 客户端对象 , 它有一个实现 DiscoveryClient ,//这个实现 DiscoveryClient 是eureka-client:1.9.3包里面的,是Euraek的服务发现的核心实现destroyMethod="shutdown") (value=EurekaClient.class, search=SearchStrategy.CURRENT) (publicEurekaClienteurekaClient(ApplicationInfoManagermanager, EurekaClientConfigconfig) { returnnewCloudEurekaClient(manager, config, this.optionalArgs, this.context); } //注册ApplicationInfoManager ,该对象用来管理 InstanceInfovalue=ApplicationInfoManager.class, search=SearchStrategy.CURRENT) (publicApplicationInfoManagereurekaApplicationInfoManager( EurekaInstanceConfigconfig) { InstanceInfoinstanceInfo=newInstanceInfoFactory().create(config); returnnewApplicationInfoManager(config, instanceInfo); } //注册EurekaRegistration,它是Eureka实例的服务注册信息,开启自动注册时,该对象才会被注册AutoServiceRegistrationProperties.class) (value="spring.cloud.service-registry.auto-registration.enabled", matchIfMissing=true) (publicEurekaRegistrationeurekaRegistration(EurekaClienteurekaClient, CloudEurekaInstanceConfiginstanceConfig, ApplicationInfoManagerapplicationInfoManager, required=false) ObjectProvider<HealthCheckHandler>healthCheckHandler) { (returnEurekaRegistration.builder(instanceConfig) .with(applicationInfoManager) .with(eurekaClient) .with(healthCheckHandler) .build(); } } //这里也是EurekaClient的配置,只是可刷新的protectedstaticclassRefreshableEurekaClientConfiguration { privateApplicationContextcontext; privateAbstractDiscoveryClientOptionalArgs<?>optionalArgs; //注册Eureak客户端EurekaClient, @Lazy 懒初始化,该bean被用到的时候会被创建//EurekaClient有一个实现 DiscoveryClient这个实现 是eureka-client:1.9.3包里面的,是Euraek的服务发现的核心实现destroyMethod="shutdown") (value=EurekaClient.class, search=SearchStrategy.CURRENT) (springframework.cloud.context.config.annotation.RefreshScope .publicEurekaClienteurekaClient(ApplicationInfoManagermanager, EurekaClientConfigconfig, EurekaInstanceConfiginstance) { manager.getInfo(); // force initializationreturnnewCloudEurekaClient(manager, config, this.optionalArgs, this.context); } //注册ApplicationInfoManager,它是InstanceInfo的管理器value=ApplicationInfoManager.class, search=SearchStrategy.CURRENT) (springframework.cloud.context.config.annotation.RefreshScope .publicApplicationInfoManagereurekaApplicationInfoManager(EurekaInstanceConfigconfig) { //注意:这里的InstanceInfo被创建的状态是InstanceStatus.STARTING;后面在初始化的过程中会修改为 InstanceStatus.UP;InstanceInfoinstanceInfo=newInstanceInfoFactory().create(config); returnnewApplicationInfoManager(config, instanceInfo); } springframework.cloud.context.config.annotation.RefreshScope .AutoServiceRegistrationProperties.class) (value="spring.cloud.service-registry.auto-registration.enabled", matchIfMissing=true) (publicEurekaRegistrationeurekaRegistration(EurekaClienteurekaClient, CloudEurekaInstanceConfiginstanceConfig, ApplicationInfoManagerapplicationInfoManager, required=false) ObjectProvider<HealthCheckHandler>healthCheckHandler) { (returnEurekaRegistration.builder(instanceConfig) .with(applicationInfoManager) .with(eurekaClient) .with(healthCheckHandler) .build(); } } ...省略... //健康指示器,用来管理服务的健康状况Health.class) (protectedstaticclassEurekaHealthIndicatorConfiguration { "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”查看到服务注册情况
publicclassServiceRegistryAutoConfiguration { ServiceRegistry.class) (Endpoint.class) (protectedclassServiceRegistryEndpointConfiguration { required=false) (privateRegistrationregistration; publicServiceRegistryEndpointserviceRegistryEndpoint(ServiceRegistryserviceRegistry) { ServiceRegistryEndpointendpoint=newServiceRegistryEndpoint(serviceRegistry); endpoint.setRegistration(registration); returnendpoint; } } } ------------------------------------------------------------------------//查看注册服务端点id="service-registry") (publicclassServiceRegistryEndpoint { privatefinalServiceRegistryserviceRegistry; privateRegistrationregistration; publicServiceRegistryEndpoint(ServiceRegistry<?>serviceRegistry) { this.serviceRegistry=serviceRegistry; } publicvoidsetRegistration(Registrationregistration) { this.registration=registration; } publicResponseEntity<?>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(); } //获取服务状态publicResponseEntitygetStatus() { if (this.registration==null) { returnResponseEntity.status(HttpStatus.NOT_FOUND).body("no registration found"); } returnResponseEntity.ok().body(this.serviceRegistry.getStatus(this.registration)); } }
AutoServiceRegistrationAutoConfiguration
是开启服务自动注册其中注入了AutoServiceRegistration
确保该类会被注册,源码如下
AutoServiceRegistrationConfiguration.class) (value="spring.cloud.service-registry.auto-registration.enabled", matchIfMissing=true) (publicclassAutoServiceRegistrationAutoConfiguration { required=false) (privateAutoServiceRegistrationautoServiceRegistration; privateAutoServiceRegistrationPropertiesproperties; protectedvoidinit() { 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; } //开启服务注册publicvoidstart() { // 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); } } publicvoidstop() { //服务停止,取消服务注册this.serviceRegistry.deregister(this.registration); this.running.set(false); } publicbooleanisRunning() { //获取服务状态是否运行returnthis.running.get(); } publicintgetPhase() { return0; } publicbooleanisAutoStartup() { returntrue; } //停止服务publicvoidstop(Runnablecallback) { stop(); callback.run(); } publicintgetOrder() { returnthis.order; } //监听web启动完成事件,执行start()方法开启服务注册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()方法ContextClosedEvent.class) (publicvoidonApplicationEvent(ContextClosedEventevent) { if( event.getApplicationContext() ==context ) { stop(); } } }
EurekaAutoServiceRegistration
监听了应用启动事件,调用EurekaServiceRegistry.register
进行服务注册,同时监听应用关闭事件,调用EurekaServiceRegistry.deregister
取消服务注册
3.EurekaServiceRegistry:服务注册器
EurekaServiceRegistry
是Eureak服务注册器,他是SpringCloud 在Netflix标准ServiceRegistry
上拓展出来的,提供了服务注册,取消注册, 关闭服务,设置服务状态,获取服务状态方法
ServiceRegistry
是EureakaServiceRegistry
的接口,他是Netflix的服务发现抽象,ServiceRegistry
需要依赖EurekaRegistration
,EurekaRegistration是ServiceInstance
的实现它约定了服务发现的实例应用的通用的信息
在应用启动时,EurekaAutoServiceRegistration
会监听应用启动完成事件,在start()方法中调用,EureakaServiceRegistry.register
方法开始服务注册,源码如下
//服务注册publicclassEurekaServiceRegistryimplementsServiceRegistry<EurekaRegistration> { privatestaticfinalLoglog=LogFactory.getLog(EurekaServiceRegistry.class); //注册方法,注册EurekaRegistrationpublicvoidregister(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(); } //取消注册,设置服务状态为downpublicvoidderegister(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() } } //设置服务注册状态publicvoidsetStatus(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); } //获取服务注册状态publicObjectgetStatus(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*/DiscoveryClient.class) //实现于DiscoveryClient (publicinterfaceEurekaClientextendsLookupService { // ========================// getters for InstanceInfo// ========================/**根据region地区获取服务列表Applications*/publicApplicationsgetApplicationsForARegion(Stringregion); /**根据服务注册地址,返回Applications*/publicApplicationsgetApplications(StringserviceUrl); /**根据地址获取服务实例信息列表*/publicList<InstanceInfo>getInstancesByVipAddress(StringvipAddress, booleansecure); /** 获取服务的远程注册状态 :InstanceStatus是用来描述服务注册状态的,状态有:UP,DOWN,STARTING,OUT_OF_SERVICE,UNKNOWN*/publicInstanceInfo.InstanceStatusgetInstanceRemoteStatus(); ...省略一些方法.. /**注册健康检查回调处理器*/publicvoidregisterHealthCheckCallback(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的启动流程
- 当程序启动,SpringBoot开启自动配置,EurekaClientAutoConfiguration Eureka客户端自动配置,和EurekaAutoServiceRegistration Eureka自动注册启动
- EurekaClientAutoConfiguration进行一些列组件的注册,包括对DiscoverClient服务发现客户端的组合,对EurekaServiceRegistry服务注册器的注册
- EurekaAutoServiceRegistration监听Web程序启动,调用EurekaServiceRegistry进行服务注册
- EurekaServiceRegistry是服务注册器,提供了服务注册,取消注册等方法,其中对DiscoverClient进行初始化,并注册健康检查处理器
- 5.DiscoveryClient服务发现客户端,负责与Eureka服务端进行交互