Spring Cloud-Eureka Client 原理解析
原文: https://blog.csdn.net/sinat_25518349/article/details/85423398
前面一些 demo 中已经介绍了如何使用 SOFABoot 来集成 Spring Cloud Netflix Eureka 组件。本篇将来先解析下 Eureka Client 的工作原理。
Netflix 和 SpringCloud
spring-cloud-commons 模块是 spring 在分布式领域上(服务发现,服务注册,断路器,负载均衡)的规范定义。spring-cloud-netflix 是基于此规范的具体实现,Netflix OSS 里的各种组件也都实现了这个 commons 规范。关系如下:
Spring Cloud Netflix Eureka 服务发现实现原理
基于上图,这里以 Eureka 中的服务发现为例,来具体讲下是如何实现的。Spring Cloud common 中提供了用于服务发现的两个关键类:DiscoveryClient 接口 和 EnableDiscoveryClient 注解。
DiscoveryClient 接口
下面这张图描述的是在服务发现这个功能上,SpringCloud 是如何与 Netflix 整合的。在 spring-cloud-netflix-eureka-client 中对 Spring Cloud Common 中的 DiscoveryClient 接口进行了实现,实现类是 EurekaDiscoveryClient 。
DiscoveryClient 的接口定义与方法:
1 | /** |
EurekaDiscoveryClient
中实现了这几个方法,但是 EurekaDiscoveryClient
自身没有实现如何与服务端交互的逻辑,而是通过 com.netflix.DiscoveryClient
类来完成。所以 spring-cloud-netflix-eureka-client
干的事情就是实现了 Spring Cloud Common
规范,然后在实现上包装了 netflix
。
@EnableDiscoveryClient 注解
1 |
|
EnableDiscoveryClientImportSelector
将会从 META-INF/spring.factories
里找出 key 为 org.springframework.cloud.client.discovery.EnableDiscoveryClient
的类。
对于 autoRegister :
- 如果自动注册属性为true,会在找出的这些类里再加上一个类:AutoServiceRegistrationConfiguration, AutoServiceRegistrationConfiguration 内部会使用@EnableConfigurationProperties(AutoServiceRegistrationProperties.class) 触发构造AutoServiceRegistrationProperties 这个 bean。像eureka,nacos,它们的自动化配置类里都使用了@ConditionalOnBean(AutoServiceRegistrationProperties.class) 来确保存在AutoServiceRegistrationProperties 这个 bean 存在的时候才会构造 AutoServiceRegistration 进行注册。
- 如果自动注册属性为 false,在Environment 里加一个 PropertySource,内部的配置项是spring.cloud.service-registry.auto-registration.enabled,值是false(代表不构造AutoServiceRegistrationProperties.class)。这样 eureka 就不会注册。
对应上面这段逻辑的代码如下:
spring-cloud-netflix-eureka-client 自己也提供了一个注解 EnableEurekaClient,其作用于这个注解一样
Eureka 架构图
- consumer : 服务消费方,eureka client 角色,可以从 eureka server 上拉取到其他已注册服务的信息,从而根据这些信息找到自己所需的服务,然后发起远程调用。
- provider : 服务提供方,eureka client 角色,可以向 eureka server 上注册和更新自己的信息,当然作为 eureka client ,它也可以从server 上获取到其他服务的信息。
- Eureka server : 服务注册中心,提供服务注册和服务发现功能;
- 同步复制 : eureka server 之间进行注册服务信息的同步,这样可以保证集群中每个server 都能提供完整的服务信息。
关于 AWS 上 Regin 和 Availability Zone 的概念,请自行查阅相关资料
源码解析
配置信息读取
Eureka Client的自动配置类是 org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration ,这里面主要就负责了一些配置信息的服务诸如 DiscoveryClient 、EurekaServiceRegistry等主要bean的初始化工作。
另外还有一个 EurekaDiscoveryClientConfiguration 类,负责配置自动注册和应用的健康检查器初始化。
读取 eureka.client.*
1 |
|
EurekaClientConfigBean 封装的是 eureka client 和 eureka server 交互所需要的配置信息,比如前面demo工程中的 eureka.client.service-url.defaultZone 的配置。
读取 eureka.instance.*
1 |
|
EurekaInstanceConfigBean 封装的是 eureka client 自身实例的配置信息,提供服务注册的基本元数据信息。
核心组件 bean 初始化
这里也实例化了一些核心的组件bean。
ApplicationInfoManager
- EurekaClientConfiguration#eurekaApplicationInfoManager
1 |
|
- RefreshableEurekaClientConfiguration#eurekaApplicationInfoManager
1
2
3
4
5
6
7
8
.springframework.cloud.context.config.annotation.RefreshScope
public ApplicationInfoManager eurekaApplicationInfoManager(EurekaInstanceConfig config) {
InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
return new ApplicationInfoManager(config, instanceInfo);
}
RefreshScope ,被此注解标注的情况下,将会被动态刷新。包括属性信息等,注意,对于动态刷新,被RefreshScope标记的类不能是final的。
ApplicationInfoManager 是应用信息管理器,用于管理服务实例的信息类 InstanceInfo 和服务实例的配置信息类 EurekaInstanceConfig 。
DiscoveryClient
1 |
|
DiscoveryClient ,前面说到,这个类是Spring Cloud 中用于服务发现使用的客户端接口。注意这里是SpringCloud提供的接口,不是netflix中的类。
EurekaServiceRegistry
1 |
|
EurekaServiceRegistry 是 ServiceRegistry 的实现类。ServiceRegistry 是 SpringCloud 提供了注册和注销等方法,这些方法允许用户提供自定义注册服务。
EurekaRegistration
1 |
|
每个 ServiceRegistry 实现都有自己的 Registry 实现。
- ZookeeperRegistration -> ZookeeperServiceRegistry
- ZookeeperRegistration -> EurekaServiceRegistry
- ConsulRegistration -> ConsulServiceRegistry
如果你需要自定义实现 ServiceRegistry ,则也不要提供一个 Registration 的实现。
服务发现
服务发现的基本情况在上面已经提到了,但是由于 SpingCloud 中并没有提供具体的交互操作而是由 com.netflix.discovery.DiscoveryClient 来完成具体工作。所以关于服务服务发现这里就直接围绕这个类来展开。
LookopService
1 | public interface LookupService<T> { |
LookupService 接口的作用就是用于查找活动服务实例;总共提供了四个方法,很好理解。每个方法的作用见注释。
EurekaClient
EurekaClient 也是一个接口,集成并且扩展了 LookupService。
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 的存在是为了给 Eureka1.x 向 Eureka 2.x 升级提供容错能力。
EurekaClient 在 LookupService 基础上扩展了很多方法,如下:
1 | public interface EurekaClient extends LookupService { |
HealthCheckHandler 这个是用于检查当前客户端状态的,这个在后面心跳机制里面会说道。
DiscoveryClient
com.netflix.discovery.DiscoveryClient,这个类会在构造函数中完成一系列重要的操作,如:拉取注册表信息,服务注册,初始化心跳机制,缓存刷新,按需注册定时任务等等。
1 | DiscoveryClient(ApplicationInfoManager applicationInfoManager, |
几个参数的释义如下:
- applicationInfoManager :应用信息管理器
- config :client 与 server 交互的配置信息
- args :客户端提供的过滤器类型(支持jersey1和jersey2),后面用来构建 EurekaTransport
- backupRegistryProvider : 备份注册中心
服务发现
下面代码片段也是在 DiscoveryClient 的构造函数里面的,这里就是拉取注册服务信息的逻辑:
1 | if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) { |
clientConfig.shouldFetchRegistry() 这个方法拿到的就是配置文件中 eureka.client.fetch-registry 的值,默认为true,表示从 eureka server 拉取注册表信息。
fetchRegistry(boolean)是从 eureka server 拉取注册信息的方法,参数用于表示是否是强制拉取全量的注册信息;此方法除非在协调eureka服务器和客户端注册表信息方面存在问题,否则此方法只尝试在第一次进行全量获取,后面均是增量获取。
fetchRegistryFromBackup() 如果 eureka server 服务不可用,则采用的备用方案。
底层通信实现 EurekaTransport
EurekaTransport 是 DiscoveryClient 的内部类,EurekaTransport 封装了具体的基于 jersey 的底层通信实现。
FetchRegistry
上图为拉取注册信息的整个过程。对于黄色贴条上的条件,如果满足其中一个,则都会进行全量拉取;否则进行增量拉取。计算 hash 值是为了后面可以与 server 端应用信息的进行对比,用于感知是否需要重新进行拉取操作。
服务注册
服务注册逻辑也是在 DiscoveryClient 的构造函数中完成,代码片段如下:
1 | if (clientConfig.shouldRegisterWithEureka() && clientConfig.shouldEnforceRegistrationAtInit()) { |
向server端注册需要满足的两个条件是:1、允许向server端注册 2、是否在客户端初始化期间强制注册
1 | boolean register() throws Throwable { |
通过 eurekaTransport 对象,基于 REST 调用向 eureka server 进行服务注册。
心跳机制
心跳机制的初始化工作也是在 DiscoveryClient 构造函数中完成。在DiscoveryClient构造函数的最后,有一个初始化调度任务的方法,在这个方法里就包括心跳的初始化。
heartbeatExecutor 心跳线程池:
1 | heartbeatExecutor = new ThreadPoolExecutor( |
scheduler 提交周期执行:
1 | // Heartbeat timer |
TimedSupervisorTask 是 eureka 中自动调节间隔的周期性任务类。HeartbeatThread 是具体执行任何的线程,run方法中执行的就是 renew() 续期。
1 | boolean renew() { |
服务下线
关闭 eureka client,还向 eureka server 发送撤销注册请求。该方法在DiscoveryClient#shutdown 方法中。
1 |
|
参考
- 《SpringCloud 微服务架构进阶》
Spring Cloud-Eureka Client 原理解析
http://www.glmapper.com/2018/12/31/springcloud/spring-cloud-eureka-client-analysis/