一.SpringCloud源码剖析-Eureka核心API
二.SpringCloud源码剖析-Eureka Client 初始化过程
三.SpringCloud源码剖析-Eureka服务注册
四.SpringCloud源码剖析-Eureka服务发现
五.SpringCloud源码剖析-Eureka Client服务续约
六.SpringCloud源码剖析-Eureka Client取消注册
七.SpringCloud源码剖析-Eureka Server的自动配置
八.SpringCloud源码剖析-Eureka Server初始化流程
九.SpringCloud源码剖析-Eureka Server服务注册流程
十.SpringCloud源码剖析-Eureka Server服务续约
十一.SpringCloud源码剖析-Eureka Server服务注册表拉取
十二.SpringCloud源码剖析-Eureka Server服务剔除
十三.SpringCloud源码剖析-Eureka Server服务下线
前言前面我们简单了解了一下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 @interface SpringBootApplication {
- @EnableAutoConfiguration 标签源码
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {...}
- AutoConfigurationImportSelector选择器源码
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { //省略部分代码...... //扫描classpath下的spring.factories自动配置 @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AnnotationAttributes attributes = 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); return StringUtils.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"}) public class EurekaClientAutoConfiguration { ...省略... //注册EurekaClientConfigBean,该类实现了EurekaClientConfig客户端配置接口,EurekaClientConfigBean封装了客户端实例Eureaka Client注册到Eureak Server 服务端所需要的注册信息,比如:服务注册地址,拉取服务注册表的时间间隔, 注册信息复制到EurekaServer的时间等等 @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; } //注册EurekaInstanceConfigBean , eureka实例配置,实现了EurekaInstanceConfig配置接口,其中包括了:获取实例ID, //获取https协议端口,获取应用名,获取应用组名,获取租约心跳间隔时间,租约到期时间,获取ip,获取主机名等等方法。 @Bean @ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT) public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils, ManagementMetadataProvider managementMetadataProvider) { String hostname = getProperty("eureka.instance.hostname"); boolean preferIpAddress = Boolean.parseBoolean(getProperty("eureka.instance.prefer-ip-address")); String ipAddress = getProperty("eureka.instance.ip-address"); boolean isSecurePortEnabled = Boolean.parseBoolean(getProperty("eureka.instance.secure-port-enabled")); String serverContextPath = env.getProperty("server.context-path", "/"); int serverPort = Integer.valueOf(env.getProperty("server.port", env.getProperty("port", "8080"))); Integer managementPort = env.getProperty("management.server.port", Integer.class);// nullable. should be wrapped into optional String managementContextPath = env.getProperty("management.server.servlet.context-path");// nullable. should be wrapped into optional Integer jmxPort = env.getProperty("com.sun.management.jmxremote.port", Integer.class);//nullable 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); } String statusPageUrlPath = getProperty("eureka.instance.status-page-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); } 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(); 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 here if(StringUtils.hasText(managementContextPath)) { instance.setHealthCheckUrlPath(managementContextPath + instance.getHealthCheckUrlPath()); instance.setStatusPageUrlPath(managementContextPath + instance.getStatusPageUrlPath()); } } setupJmxPort(instance, jmxPort); return instance; } //注册DiscoveryClient服务发现客户端,它是spring-cloud-commons:2.0.1.RELEASE SpringCloud公共抽象包中的(org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient), //是Spring Cloudd的服务发现抽象接口 EurekaDiscoveryClient是它的实现 @Bean public DiscoveryClient discoveryClient(EurekaInstanceConfig config, EurekaClient client) { return new EurekaDiscoveryClient(config, client); } //Eureak服务注册器,他是SpringCloud 在Netflix标准上拓展出来的,提供了服务注册,取消注册,设置服务状态,获取服务状态方法,ServiceRegistry负责服务注册,DiscoveryClient负责服务发现 @Bean public EurekaServiceRegistry eurekaServiceRegistry() { return new EurekaServiceRegistry(); } //EurekaAutoServiceRegistration :eureka服务自动注册器 @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); } //取消注册 @Override public void stop() { this.serviceRegistry.deregister(this.registration); this.running.set(false); } //EureakClient配置,该配置没有刷新功能 @Configuration @ConditionalOnMissingRefreshScope //不可刷新 protected static class EurekaClientConfiguration { @Autowired private ApplicationContext context; @Autowired private AbstractDiscoveryClientOptionalArgs<?> optionalArgs; //注册EurekaClient 客户端对象 , 它有一个实现 DiscoveryClient , //这个实现 DiscoveryClient 是eureka-client:1.9.3包里面的,是Euraek的服务发现的核心实现 @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); } //注册ApplicationInfoManager ,该对象用来管理 InstanceInfo @Bean @ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT) public ApplicationInfoManager eurekaApplicationInfoManager( EurekaInstanceConfig config) { InstanceInfo instanceInfo = new InstanceInfoFactory().create(config); return new ApplicationInfoManager(config, instanceInfo); } //注册EurekaRegistration,它是Eureka实例的服务注册信息,开启自动注册时,该对象才会被注册 @Bean @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(); } } //这里也是EurekaClient的配置,只是可刷新的 @Configuration @ConditionalOnRefreshScope protected static class RefreshableEurekaClientConfiguration { @Autowired private ApplicationContext context; @Autowired private AbstractDiscoveryClientOptionalArgs<?> 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 @Lazy public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config, EurekaInstanceConfig instance) { manager.getInfo(); // force initialization return new CloudEurekaClient(manager, config, this.optionalArgs, this.context); } //注册ApplicationInfoManager,它是InstanceInfo的管理器 @Bean @ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT) @org.springframework.cloud.context.config.annotation.RefreshScope @Lazy public ApplicationInfoManager eurekaApplicationInfoManager(EurekaInstanceConfig config) { //注意:这里的InstanceInfo被创建的状态是InstanceStatus.STARTING;后面在初始化的过程中会修改为 InstanceStatus.UP; InstanceInfo instanceInfo = new InstanceInfoFactory().create(config); return new ApplicationInfoManager(config, instanceInfo); } @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(); } } ...省略... //健康指示器,用来管理服务的健康状况 @Configuration @ConditionalOnClass(Health.class) protected static class EurekaHealthIndicatorConfiguration { @Bean @ConditionalOnMissingBean @ConditionalOnEnabledHealthIndicator("eureka") public EurekaHealthIndicator eurekaHealthIndicator(EurekaClient eurekaClient, EurekaInstanceConfig instanceConfig, EurekaClientConfig clientConfig) { return new EurekaHealthIndicator(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.jarSpringCloud公共包中/META-INF/spring.factories配置文件中有AutoServiceRegistrationAutoConfiguration自动配置类,当应用启动SpringBoot会扫描到该自动配置类,然后注册到Spring容器
# AutoConfiguration org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,\ org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration
ServiceRegistryAutoConfiguration是配置注册的服务的端点信息,可通过端点 “/service-registry”查看到服务注册情况
@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(registration); return endpoint; } } } ------------------------------------------------------------------------ //查看注册服务端点 @Endpoint(id = "service-registry") public class ServiceRegistryEndpoint { private final ServiceRegistry serviceRegistry; private Registration registration; public ServiceRegistryEndpoint(ServiceRegistry<?> serviceRegistry) { this.serviceRegistry = serviceRegistry; } public void setRegistration(Registration registration) { this.registration = registration; } @WriteOperation public ResponseEntity<?> setStatus(String status) { Assert.notNull(status, "status may not by null"); if (this.registration == null) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("no registration found"); } this.serviceRegistry.setStatus(this.registration, status); return ResponseEntity.ok().build(); } //获取服务状态 @ReadOperation public ResponseEntity getStatus() { if (this.registration == null) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("no registration found"); } return ResponseEntity.ok().body(this.serviceRegistry.getStatus(this.registration)); } }
AutoServiceRegistrationAutoConfiguration是开启服务自动注册其中注入了AutoServiceRegistration确保该类会被注册,源码如下
@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 (autoServiceRegistration == null && this.properties.isFailFast()) { throw new IllegalStateException("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 源码如下:
//自动注册器 public class EurekaAutoServiceRegistration implements AutoServiceRegistration, SmartLifecycle, Ordered { private static final Log log = LogFactory.getLog(EurekaAutoServiceRegistration.class); private AtomicBoolean running = new AtomicBoolean(false); private int order = 0; private AtomicInteger port = new AtomicInteger(0); private ApplicationContext context; //服务注册器 private EurekaServiceRegistry serviceRegistry; //服务注册信息登记 private EurekaRegistration registration; public EurekaAutoServiceRegistration(ApplicationContext context, EurekaServiceRegistry serviceRegistry, EurekaRegistration registration) { this.context = context; this.serviceRegistry = serviceRegistry; this.registration = registration; } //开启服务注册 @Override public void start() { // only set the port if the nonSecurePort or securePort is 0 and this.port != 0 if (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 below if (!this.running.get() && this.registration.getNonSecurePort() > 0) { //调用服务注册器进行服务注册 this.serviceRegistry.register(this.registration); //发布一个服务注册事件 this.context.publishEvent( new InstanceRegisteredEvent<>(this, this.registration.getInstanceConfig())); this.running.set(true); } } @Override public void stop() { //服务停止,取消服务注册 this.serviceRegistry.deregister(this.registration); this.running.set(false); } @Override public boolean isRunning() { //获取服务状态是否运行 return this.running.get(); } @Override public int getPhase() { return 0; } @Override public boolean isAutoStartup() { return true; } //停止服务 @Override public void stop(Runnable callback) { stop(); callback.run(); } @Override public int getOrder() { return this.order; } //监听web启动完成事件,执行start()方法开启服务注册 @EventListener(WebServerInitializedEvent.class) public void onApplicationEvent(WebServerInitializedEvent event) { // TODO: take SSL into account int localPort = event.getWebServer().getPort(); if (this.port.get() == 0) { log.info("Updating port to " + localPort); this.port.compareAndSet(0, localPort); start(); } } //监听应用关闭事件,执行stop()方法 @EventListener(ContextClosedEvent.class) public void onApplicationEvent(ContextClosedEvent event) { 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方法开始服务注册,源码如下
//服务注册 public class EurekaServiceRegistry implements ServiceRegistry<EurekaRegistration> { private static final Log log = LogFactory.getLog(EurekaServiceRegistry.class); //注册方法,注册EurekaRegistration @Override public void register(EurekaRegistration reg) { //初始化客户端,该方法会触发 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)的初始化 private void maybeInitializeClient(EurekaRegistration reg) { // force initialization of possibly scoped proxies reg.getApplicationInfoManager().getInfo(); //初始化Eurak客户端 reg.getEurekaClient().getApplications(); } //取消注册,设置服务状态为down @Override public void deregister(EurekaRegistration reg) { 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() } } //设置服务注册状态 @Override public void setStatus(EurekaRegistration registration, String status) { InstanceInfo info = 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.InstanceStatus newStatus = InstanceInfo.InstanceStatus.toEnum(status); registration.getEurekaClient().setStatus(newStatus, info); } //获取服务注册状态 @Override public Object getStatus(EurekaRegistration registration) { String appname = registration.getInstanceConfig().getAppname(); String instanceId = registration.getInstanceConfig().getInstanceId(); InstanceInfo info = registration.getEurekaClient().getInstanceInfo(appname, instanceId); HashMap<String, Object> status = new HashMap<>(); if (info != null) { status.put("status", info.getStatus().toString()); status.put("overriddenStatus", info.getOverriddenStatus().toString()); } else { status.put("status", UNKNOWN.toString()); } return status; } public void close() { } }
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. * 用来查找活动的服务实例 */ public interface LookupService<T> { /** 根据应用名获取该名字对应的应用,Application中描述了该appName对应的多个应用实例 */ Application getApplication(String appName); /** 获取所有的应用注册列表,Applications是eureka server返回的所有注册信息 */ Applications getApplications(); /** 根据ID获取服务实例信息信息对象列表 */ List<InstanceInfo> getInstancesById(String id); InstanceInfo getNextServerFromEureka(String virtualHostname, boolean secure); }
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) //实现于DiscoveryClient public interface EurekaClient extends LookupService { // ======================== // getters for InstanceInfo // ======================== /** 根据region地区获取服务列表Applications */ public Applications getApplicationsForARegion(@Nullable String region); /** 根据服务注册地址,返回Applications */ public Applications getApplications(String serviceUrl); /** 根据地址获取服务实例信息列表 */ public List<InstanceInfo> getInstancesByVipAddress(String vipAddress, boolean secure); /** 获取服务的远程注册状态 :InstanceStatus是用来描述服务注册状态的, 状态有:UP,DOWN,STARTING,OUT_OF_SERVICE,UNKNOWN */ public InstanceInfo.InstanceStatus getInstanceRemoteStatus(); ...省略一些方法.. /** 注册健康检查回调处理器 */ @Deprecated public void registerHealthCheckCallback(HealthCheckCallback callback); /** 注册健康检查处理器 */ public void registerHealthCheck(HealthCheckHandler healthCheckHandler); ...省略... //注册eureak监听器,监听eureka内部状态变化 public void registerEventListener(EurekaEventListener eventListener); /** Eureak客户端down机,从Eureak服务端取消服务注册 */ public void shutdown(); ...省略...
该接口是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服务器上注册的服务/实例列表
这里总结一个Eureak的启动流程
- 当程序启动,SpringBoot开启自动配置,EurekaClientAutoConfiguration Eureka客户端自动配置,和EurekaAutoServiceRegistration Eureka自动注册启动
- EurekaClientAutoConfiguration进行一些列组件的注册,包括对DiscoverClient服务发现客户端的组合,对EurekaServiceRegistry服务注册器的注册
- EurekaAutoServiceRegistration监听Web程序启动,调用EurekaServiceRegistry进行服务注册
- EurekaServiceRegistry是服务注册器,提供了服务注册,取消注册等方法,其中对DiscoverClient进行初始化,并注册健康检查处理器
- 5.DiscoveryClient服务发现客户端,负责与Eureka服务端进行交互