- org.springframework.cloud:spring-cloud-starter-netflix-eureka-server:2.1.5.RELEASE
点击查看【processon】
注册中心核心流程分为两类
- 服务提供者注册信息,其中包括:
注册
、健康检查
、更新服务信息
- 服务消费者获取注册表:包括:
拉取注册表
,订阅更新
针对两大流程,Eureka 提供了一系列对应 API,通过API 分析 Eureka 部分流程。
一、服务注册信息抽象对象
Eureka 中保存了从各个网络中注册上来的服务信息,而服务信息需要特定的对象进行抽象存储
1.1、InstanceInfo
Eureka 原生数据模型
服务注册到 Eureka 中的数据模型如上图,
数据模型对应 Eureka 中的 com.netflix.appinfo.InstanceInfo
,其相关属性如下图:
大部分属性通过对比 REST API /eureka/apps/{appID}/{instanceID}
中返回的值和属性名称能够得出其大体的意思和作用,下面单独看看几个特别的属性
LeaseInfo
租赁信息
public class LeaseInfo {
public static final int DEFAULT_LEASE_RENEWAL_INTERVAL = 30;
public static final int DEFAULT_LEASE_DURATION = 90;
// Client settings
/** Client 端续约的时间间隔,默认:30 s **/
private int renewalIntervalInSecs = DEFAULT_LEASE_RENEWAL_INTERVAL;
/** Client 端需要设定的租约的有效时长,默认:90s **/
private int durationInSecs = DEFAULT_LEASE_DURATION;
// Server populated
/** Server 端设置的该租约第一次注册时间 **/
private long registrationTimestamp;
/** Server 端设置的该租约的最后一次续约时间 **/
private long lastRenewalTimestamp;
/** Server 端设置的该租约被剔除的时间 **/
private long evictionTimestamp;
/** Server 端设置的该服务实例标记为 UP 的时间 **/
private long serviceUpTimestamp;
}
HashMap metadata
元数据信息
元信息本身是一个 HashMap
用来存储配置信息,如:Spring Boot Admin 授权账号密码。
Spring Cloud Discovery 适配了 Zookeeper 、 Consul、Eureka 等注册中心,所以对服务实例进行抽象定义。
ServiceInstance
相关实现如下:
二、Eureka API
Eureka Server API 基本操作包括如下:
API 相关操作实现类都在
com.netflix.eureka.resources
包下
- 服务注册
com.netflix.eureka.resources.ApplicationResource#addInstance
- 服务下线
com.netflix.eureka.resources.InstanceResource#cancelLease
- 服务续租
com.netflix.eureka.resources.InstanceResource#renewLease
- 服务更新
- 状态更新:
com.netflix.eureka.resources.InstanceResource#statusUpdate
- Metadata 更新:
com.netflix.eureka.resources.InstanceResource#updateMetadata
- 状态更新:
上述相关 API 的执行,最终都会经由注册表
PeerAwareInstanceRegistryImpl
接口实现类完成,以 InstanceRegistry
为例来看一下。InstanceRegistry
类图结构如下:
通过类结构追述到其两个顶层接口定义,LeaseManager
和 LookupService
这个两个接口对 Eureka 注册、下线、续租、剔除,查询等进行了定义,相关方法定义如下:
LeaseManager<T>
租赁管理接口 ,定义了 服务注册、剔除、续租、删除服务。
- register :用于注册服务实例信息
- cancel:用于删除服务实例
- renew:与 Eureka Server 进行心跳操作,维持租约
-
LookupService<T>
主要是给 Client 端提供服务实例查询方法的 API 定义。
getApplication(String appName)
- getApplications()
- getInstancesById(String id)
getNextServerFromEureka(String virtualHostname, boolean secure)
2.1、服务注册
2.1.1、Eureka Server API
Eureka Server 对外提供的注册 API 具体实现
com.netflix.eureka.resources.ApplicationResource#addInstance
如上述代码所示,接口主要接收两个参数InstanceInfo
即注册上来的相关服务信息,例如:- isReplication
本次请求是否为其他 Eureka Server 节点副本复制请求
高可用部署时,如果请求不是来自 Eureka Server ,而是来自 Eureka Client 则需要同步到其他 Eureka Server 节点
忽略其他判定处理,最终注册由 com.netflix.eureka.registry.PeerAwareInstanceRegistry#register
处理,而后存储到注册表中,如下:
2.1.2、Eureka Client
Eureka Client 开启起源于注解
@EnableDiscoveryClient
最终所有 Client 相关操作发起由AbstractJerseyEurekaHttpClient
完成
Eureka Client 向 Eureka Server 发起注册最终操作方法 com.netflix.discovery.shared.transport.jersey.AbstractJerseyEurekaHttpClient#register
,相关代码如下:
如上述代码所示,通过构建当前 Client 端相关服务信息构建 InstanceInfo
对象信息,然后拼接 URL ,向 Eureka Server 发起注册调用,并得到响应,204 表示注册成功
2.2、服务注册列表拉取
对于消费者来说,需要获取到注册列表,以此来或者需要调用的服务相关信息。
2.2.1、Eureka Server API
Eureka 提供了一系列获取服务注册信息的 API,大体可以分为两种
- 直接从注册表中获取注册信息
如:com.netflix.eureka.resources.InstancesResource#getById
根据id直接从注册表中获取注册信息 - 从只读缓存中读取数据
如:com.netflix.eureka.resources.ApplicationsResource#getContainers
获取全量注册信息
如:com.netflix.eureka.resources.ApplicationsResource#getContainerDifferential
获取增量注册信息
以 Eureka Server 提供的全量拉取API com.netflix.eureka.resources.ApplicationsResource#getContainers
为例。com.netflix.eureka.resources.ApplicationsResource#getContainers
接口数据的获取最终从 ResponseCache
中获取,相关代码如下:ResponseCache
三级缓存架构实现 了注册信息的高速读写操作,不过也带来了一定的信息延迟。
返回的信息例如:com.netflix.eureka.registry.ResponseCacheImpl#getGZIP
[具体三级缓存架构参考]
2.2.1、Client 对 Eureka Server 发起拉取注册表的请求
同[服务注册 Eureka Client操作 ] ,注册表信息的拉取同样有 com.netflix.discovery.shared.transport.jersey.AbstractJerseyEurekaHttpClient
完成,具体方法入口为 com.netflix.discovery.shared.transport.jersey.AbstractJerseyEurekaHttpClient#getApplications
,相关代码如下:
三、扩展:Eureka 注册信息拉取多级缓存架构
服务的注册、订阅可能存在延迟问题,比如:已经注册的服务,无法马上被感知等。 这写问题的根源之一就是 Eureka Server 端数据缓存问题。
Eureka Server 中存储的注册信息在注册表中,同时缓存了两份数据
- 只读缓存(用来对外提供读操作)
- 读写缓存(对内提供写操作)
两级缓存 加上 原本存储在内存中的注册表元数据 com.netflix.eureka.registry.AbstractInstanceRegistry#registry
三者组合构成了注册表的三级缓存信息。
获取实例入口
以 Eureka API 作为入口,进行源码追踪,以 /eureka/apps : 请求所有实例数据
作为入口。
对应的请求实现类为 com.netflix.eureka.resources.ApplicationsResource#getContainers
ResponseCache#getGZIP
的获取逻辑为:
- 先从只读缓存
readOnlyCacheMap
中获取 - 如果只读缓存为空,从读写缓存中获取
readWriteCacheMap
其中两个关键点
- 1、只读缓存
readOnlyCacheMap
通过定时器维护,定时从读写缓存readWriteCacheMap
中读取到只读缓存中
默认 30s 执行一次。
定时器代码在ResponseImpl
构造函数中,代码如下:
具体业务操作在com.netflix.eureka.registry.ResponseCacheImpl#getCacheUpdateTask
中。 - 2、读写缓存更新时机
- 服务注册、下线、更新等操作触发刷新读写缓存
如:取消续租(代码:com.netflix.eureka.resources.InstanceResource#cancelLease
)
最终都会调用ResponseCache#invalidate
更新读写缓存信息。
- 服务注册、下线、更新等操作触发刷新读写缓存
从上述可以得到结论
- 1、Eureka 注册数据,通过二级缓存对外提供访问,本地注册表不提供直接访问。
2、Eureka 注册信息对外提供服务的为一级缓存(只读缓存),只读缓存失效默认 30s ,所以存在大多30s的注册信息延迟。
四、扩展:Eureka Server Replication
Eureka Server 本身提供了高可用部署,多个 Eureka Server 通过同步复制的方式保证格个 Eureka Server 注册表的最终一致性。
《Eureka API》介绍了 Eureka 相关交互的API,如:注册、续租、状态变更、服务下线等。
关于注册表信息的变更操作 API,都会接收一个参数@HeaderParam(PeerEurekaNode.**_HEADER_REPLICATION_**) String isReplication
true :表示当前请求由其他 Eureka Server 复制而来
- false:表示当前请求由 Eureka Client 请求而来,处理完请求后,如果 Eureka Server 为高可用部署,需要考虑同步注册信息。
服务注册为例
如图,此时部署了两台 Eureka Server ,且两台 Eureka Server 相互注册,组成2节点的高可用架构。
此时有一台需要注册客户端服务,注册到其中一台 Eureka Server ,注册后,Eureka Server 间会进行注册信息的同步。
客户端注册服务到 Eureka Server 中
注册代码入口:
com.netflix.eureka.resources.ApplicationResource#addInstance
相关逻辑线索如下:
上图流程步骤
- 注册到当前 Eureka Server 注册表中
- 判定是否进行注册表同步
- Eureka Server 节点为集群部署 && isReplication = false(即:请求来自 Eureka Client)
接着,继续查看节点注册节点复制的流程,相关代码com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#replicateInstanceActionsToPeers
如下:
如上述代码所示
- 根据不同的操作行为,执行相关节点的相关操作
以注册为例,最终调用的为 PeerEurekaNode#register
方法。
Eureka 节点的复制最终调用的 JerseyReplicationClient#register
,该方法拼接实例信息,调用了Eureka Server 的服务实例注册 API,对比 Eureka Client 的注册,Eureka Server 的注册表同步,中, Header 中的**x-netflix-discovery-replication=true**
参数不同,相关设置代码在发起 API 调用前的 Header 设置,代码如下: