一.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 configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set 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 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) {
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) {
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服务注册表等方法
在spring-cloud-commons-2.0.1.RELEASE.jar
SpringCloud公共包中/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
取消服务注册
EurekaServiceRegistry
是Eureak服务注册器,他是SpringCloud 在Netflix标准ServiceRegistry
上拓展出来的,提供了服务注册,取消注册, 关闭服务,设置服务状态,获取服务状态方法
ServiceRegistry
是EureakaServiceRegistry
的接口,他是Netflix的服务发现抽象,ServiceRegistry
需要依赖EurekaRegistration
,EurekaRegistration是ServiceInstance
的实现它约定了服务发现的实例应用的通用的信息
在应用启动时,EurekaAutoServiceRegistration
会监听应用启动完成事件,在start()方法中调用,EureakaServiceRegistry.register
方法开始服务注册,源码如下
//服务注册
public class EurekaServiceRegistry implements ServiceRegistry {
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 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
这个类来源于 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 {
/**
根据应用名获取该名字对应的应用,Application中描述了该appName对应的多个应用实例
*/
Application getApplication(String appName);
/**
获取所有的应用注册列表,Applications是eureka server返回的所有注册信息
*/
Applications getApplications();
/**
根据ID获取服务实例信息信息对象列表
*/
List 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 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服务端进行交互