• org.springframework.cloud:spring-cloud-starter-netflix-eureka-server:2.1.5.RELEASE

点击查看【processon】
注册中心核心流程分为两类

  • 服务提供者注册信息,其中包括:注册健康检查更新服务信息
    [spring-cloud]-Eureka源码分析 - 图1
  • 服务消费者获取注册表:包括:拉取注册表订阅更新
    [spring-cloud]-Eureka源码分析 - 图2

针对两大流程,Eureka 提供了一系列对应 API,通过API 分析 Eureka 部分流程。

一、服务注册信息抽象对象

Eureka 中保存了从各个网络中注册上来的服务信息,而服务信息需要特定的对象进行抽象存储

1.1、InstanceInfo Eureka 原生数据模型

15.png

服务注册数据模型

服务注册到 Eureka 中的数据模型如上图,
数据模型对应 Eureka 中的 com.netflix.appinfo.InstanceInfo,其相关属性如下图:
01.png
大部分属性通过对比 REST API /eureka/apps/{appID}/{instanceID} 中返回的值和属性名称能够得出其大体的意思和作用,下面单独看看几个特别的属性

LeaseInfo 租赁信息

  1. public class LeaseInfo {
  2. public static final int DEFAULT_LEASE_RENEWAL_INTERVAL = 30;
  3. public static final int DEFAULT_LEASE_DURATION = 90;
  4. // Client settings
  5. /** Client 端续约的时间间隔,默认:30 s **/
  6. private int renewalIntervalInSecs = DEFAULT_LEASE_RENEWAL_INTERVAL;
  7. /** Client 端需要设定的租约的有效时长,默认:90s **/
  8. private int durationInSecs = DEFAULT_LEASE_DURATION;
  9. // Server populated
  10. /** Server 端设置的该租约第一次注册时间 **/
  11. private long registrationTimestamp;
  12. /** Server 端设置的该租约的最后一次续约时间 **/
  13. private long lastRenewalTimestamp;
  14. /** Server 端设置的该租约被剔除的时间 **/
  15. private long evictionTimestamp;
  16. /** Server 端设置的该服务实例标记为 UP 的时间 **/
  17. private long serviceUpTimestamp;
  18. }

HashMap metadata 元数据信息

元信息本身是一个 HashMap
用来存储配置信息,如:Spring Boot Admin 授权账号密码。

Spring Cloud Discovery 适配了 Zookeeper 、 Consul、Eureka 等注册中心,所以对服务实例进行抽象定义。 ServiceInstance 相关实现如下: 02.png

二、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类图结构如下:
03.png
通过类结构追述到其两个顶层接口定义,LeaseManagerLookupService
这个两个接口对 Eureka 注册、下线、续租、剔除,查询等进行了定义,相关方法定义如下:

LeaseManager<T>

租赁管理接口 ,定义了 服务注册、剔除、续租、删除服务。

  • register :用于注册服务实例信息
  • cancel:用于删除服务实例
  • renew:与 Eureka Server 进行心跳操作,维持租约
  • evict: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
    image.png
    如上述代码所示,接口主要接收两个参数

  • InstanceInfo
    即注册上来的相关服务信息,例如:
    image.png

  • isReplication
    本次请求是否为其他 Eureka Server 节点副本复制请求
    高可用部署时,如果请求不是来自 Eureka Server ,而是来自 Eureka Client 则需要同步到其他 Eureka Server 节点

忽略其他判定处理,最终注册由 com.netflix.eureka.registry.PeerAwareInstanceRegistry#register处理,而后存储到注册表中,如下:
image.png

2.1.2、Eureka Client

Eureka Client 开启起源于注解 @EnableDiscoveryClient 最终所有 Client 相关操作发起由 AbstractJerseyEurekaHttpClient完成

Eureka Client 向 Eureka Server 发起注册最终操作方法 com.netflix.discovery.shared.transport.jersey.AbstractJerseyEurekaHttpClient#register,相关代码如下:
image.png
如上述代码所示,通过构建当前 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中获取,相关代码如下:
image.png
ResponseCache三级缓存架构实现 了注册信息的高速读写操作,不过也带来了一定的信息延迟。
返回的信息例如:com.netflix.eureka.registry.ResponseCacheImpl#getGZIP
image.png
[具体三级缓存架构参考]

2.2.1、Client 对 Eureka Server 发起拉取注册表的请求

同[服务注册 Eureka Client操作 ] ,注册表信息的拉取同样有 com.netflix.discovery.shared.transport.jersey.AbstractJerseyEurekaHttpClient完成,具体方法入口为 com.netflix.discovery.shared.transport.jersey.AbstractJerseyEurekaHttpClient#getApplications,相关代码如下:
image.png

三、扩展:Eureka 注册信息拉取多级缓存架构

服务的注册、订阅可能存在延迟问题,比如:已经注册的服务,无法马上被感知等。 这写问题的根源之一就是 Eureka Server 端数据缓存问题。

image.png
Eureka Server 中存储的注册信息在注册表中,同时缓存了两份数据

  • 只读缓存(用来对外提供读操作)
  • 读写缓存(对内提供写操作)

两级缓存 加上 原本存储在内存中的注册表元数据 com.netflix.eureka.registry.AbstractInstanceRegistry#registry

三者组合构成了注册表的三级缓存信息。

获取实例入口

以 Eureka API 作为入口,进行源码追踪,以 /eureka/apps : 请求所有实例数据作为入口。
对应的请求实现类为 com.netflix.eureka.resources.ApplicationsResource#getContainers
image.png
ResponseCache#getGZIP 的获取逻辑为:

  • 先从只读缓存 readOnlyCacheMap中获取
  • 如果只读缓存为空,从读写缓存中获取 readWriteCacheMap

image.png
其中两个关键点

  • 1、只读缓存 readOnlyCacheMap通过定时器维护,定时从读写缓存readWriteCacheMap中读取到只读缓存中
    默认 30s 执行一次。
    定时器代码在 ResponseImpl构造函数中,代码如下:
    image.png
    具体业务操作在 com.netflix.eureka.registry.ResponseCacheImpl#getCacheUpdateTask中。
  • 2、读写缓存更新时机
    • 服务注册、下线、更新等操作触发刷新读写缓存
      如:取消续租(代码:com.netflix.eureka.resources.InstanceResource#cancelLease
      最终都会调用 ResponseCache#invalidate更新读写缓存信息。

从上述可以得到结论

  • 1、Eureka 注册数据,通过二级缓存对外提供访问,本地注册表不提供直接访问。
  • 2、Eureka 注册信息对外提供服务的为一级缓存(只读缓存),只读缓存失效默认 30s ,所以存在大多30s的注册信息延迟。

    四、扩展:Eureka Server Replication

    image.png
    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 为高可用部署,需要考虑同步注册信息。

服务注册为例

image.png
如图,此时部署了两台 Eureka Server ,且两台 Eureka Server 相互注册,组成2节点的高可用架构。
此时有一台需要注册客户端服务,注册到其中一台 Eureka Server ,注册后,Eureka Server 间会进行注册信息的同步。

客户端注册服务到 Eureka Server 中

注册代码入口:com.netflix.eureka.resources.ApplicationResource#addInstance

相关逻辑线索如下:
image.png
上图流程步骤

  • 注册到当前 Eureka Server 注册表中
  • 判定是否进行注册表同步
    • Eureka Server 节点为集群部署 && isReplication = false(即:请求来自 Eureka Client)

接着,继续查看节点注册节点复制的流程,相关代码
com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#replicateInstanceActionsToPeers如下:
image.png
如上述代码所示

  • 根据不同的操作行为,执行相关节点的相关操作

以注册为例,最终调用的为 PeerEurekaNode#register方法。
image.png
Eureka 节点的复制最终调用的 JerseyReplicationClient#register,该方法拼接实例信息,调用了Eureka Server 的服务实例注册 API,对比 Eureka Client 的注册,Eureka Server 的注册表同步,中, Header 中的**x-netflix-discovery-replication=true**
参数不同,相关设置代码在发起 API 调用前的 Header 设置,代码如下:
image.png