Eureka Client的初始化

Eureka Client与应用程序相结合,提供了服务注册、续期、获取注册表等功能。 相比于Eureka Server,因为它是无状态的,因此实现上也简单很多

我们一起来看看,源码中Eureka Client是如何启动的?

一 核心类DiscoveryClient

来看源码中注释:

该类是与Eureka Server交互的工具。Eureka客户端负责:

a)在Eureka服务器上注册实例

b)与Eureka服务器续签租约

c)在关闭期间从Eureka服务器取消租约

d)查询在Eureka Server上注册的服务/实例列表

Eureka Client需要一个配置好的Eureka Server url列表 来与之对话。这些java.net.url通常是不会更改的amazon弹性ip。在与某个Eureka Server交互失败时,都将故障转移到列表中其他的Eureka Server url。

如下Eureka Client的配置,Eureka Server url就是指serviceUrl,当其中某个地址不通时,将使用其他url,即故障转移

yaml 复制代码
# eureka client配置示例
eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    serviceUrl:
      # 当Eureka Server集群部署时,此处应配置多个url
      defaultZone: http://192.168.2.4:8761/eureka/,http://192.168.2.125:8761/eureka/

在DiscoveryClient的构造方法中,完成了Eureka Client的启动。

二、初始化过程

启动过程,其实就是创建一个DiscoveryClient实例的过程。

1.1 DiscoveryClient的构造方法

  • 构造器中,提供了一个创建BackupRegistry子类实例的Provider;

BackupRegistry接口,是在client无法从任何eureka Server获取注册表时,提供一个配置的注册表。默认采用空实现。

  • ApplicationInfoManager:管理当前应用节点自身信息,如ipAddr、port、status等,这些信息用于注册,或被其他服务发现;
  • EurekaClientConfig:eureka客户端向eureka服务器注册实例所需的配置信息 。大多数必需的信息由默认配置DefaultEurekaClientConfig提供,用户只需要提供eureka服务器服务url

2.2 创建空注册表

java 复制代码
private final AtomicReference<Applications> localRegionApps = new AtomicReference<Applications>();
// 创建空应用集
localRegionApps.set(new Applications());

Applications是对eureka server返回的注册表的封装,关键属性有:应用队列、注册表。 而Application用于保存某个应用程序的节点列表,有以下属性。 再来看InstanceInfo类,是对某个应用集群某个节点的封装 ,关键属性是节点的网络地址:instanceId、appName、ipAddr和port。

2.3 保存serviceUrl和创建Transport

java 复制代码
// 对serviceUrl的封装
remoteRegionsToFetch = new AtomicReference<String>(clientConfig.fetchRegistryForRemoteRegions());  
remoteRegionsRef = new AtomicReference<>(remoteRegionsToFetch.get() == null ? null : remoteRegionsToFetch.get().split(","));

EurekaTransport是DiscoveryClient中的内部类,用于创建和持有与Eureka Server交互的HttpClient:分别创建了用于fetch、pull的Client。

java 复制代码
eurekaTransport = new EurekaTransport();  
scheduleServerEndpointTask(eurekaTransport, args);

TransportClientFactories有两个实现,默认使用Jersey1TransportClientFactories

2.4 创建线程池

当shouldRegisterWithEureka、shouldFetchRegistry都为false时,会提前return。否则,创建3个线程池。 将在后面看到对它们的使用。

java 复制代码
// 调用线程池
private final ScheduledExecutorService scheduler;
// 执行注册、心跳请求
private final ThreadPoolExecutor heartbeatExecutor;
// 刷新Eureka client端缓存的注册表
private final ThreadPoolExecutor cacheRefreshExecutor;

2.5 从Eureka Server拉取注册表

java 复制代码
// 从Eureka Server拉取注册表
if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) {
  // 失败时通过BackupRegistry获取
  fetchRegistryFromBackup();
}

注意,源码中"是否拉取注册表",配置名是shouldFetchRegistry,在springboot整合包中叫fetchRegistry,默认为true。

来看fetchRegistry(),在第一次获取注册表时为全量拉取;之后只获取增量。

java 复制代码
// 简化后代码
if (全量拉取){
	getAndStoreFullRegistry();
} else {
	getAndUpdateDelta(applications);
}

2.6 启动周期任务

调用DiscoveryClient.initScheduledTasks(),创建了刷新注册表、续租、节点信息更新同步任务。

  • 如果shouldFetchRegistry=true,则添加周期刷新注册表任务
java 复制代码
// 周期刷新注册表
scheduler.schedule(
        new TimedSupervisorTask(
                "cacheRefresh",
                scheduler,
                cacheRefreshExecutor,
                registryFetchIntervalSeconds,
                TimeUnit.SECONDS,
                expBackOffBound,
                new CacheRefreshThread()
        ),
        registryFetchIntervalSeconds, TimeUnit.SECONDS);
  • 如果shouldRegisterWithEureka为true,则创建心跳周期任务
java 复制代码
// Heartbeat timer
scheduler.schedule(
        new TimedSupervisorTask(
                "heartbeat",
                scheduler,
                heartbeatExecutor,
                renewalIntervalInSecs,
                TimeUnit.SECONDS,
                expBackOffBound,
                new HeartbeatThread()
        ),
        renewalIntervalInSecs, TimeUnit.SECONDS);
  • 注册StatusChangeListener 当配置shouldOnDemandUpdateStatusChange=true时,将在节点状态变为DOWN时,立即同步节点信息给Eureka。

  • 启动节点信息更新同步任务 创建 InstanceInfoReplicator实例,用于更新该节点的InstanceInfo(如元数据、节点状态等),将其同步到Eureka server。

    • 当节点信息发生变化时,设置InstanceInfo.isInstanceInfoDirty=true和lastDirtyTimestamp;
    • 使用仅有1个线程的ScheduledThreadPool,来周期检查isInstanceInfoDirty标志
    • 节点信息更新同步,是通过发起一次注册来实现;

2.7 启动监控任务

三、总结

  • DiscoveryClient是核心资源持有者,包括应用集、注册表、线程池、Http客户端等;
  • Eureka Client的启动过程,就是创建DiscoveryClient示例的过程;
  • 启动时,初始化一些全局资源,从Eureka初次拉取注册表,启动刷新注册表本地缓存、续租、节点信息变更同步这3个周期任务;

在随后的文章中,将进一步熟悉客户端注册、续租等的实现。

相关推荐
Be_Somebody1 分钟前
[这可能是最好的Spring教程!]Maven的模块管理——如何拆分大项目并且用parent继承保证代码的简介性
java·spring boot·spring·spring入门
一个数据小开发17 分钟前
业务开发问题之ConcurrentHashMap
java·开发语言·高并发·map
会飞的架狗师33 分钟前
【Spring】Spring框架中有有哪些常见的设计模式
java·spring·设计模式
Jakarta EE44 分钟前
在JPA和EJB中用乐观锁解决并发问题
java
花心蝴蝶.1 小时前
并发编程中常见的锁策略
java·jvm·windows
A_cot1 小时前
一篇Spring Boot 笔记
java·spring boot·笔记·后端·mysql·spring·maven
tryCbest2 小时前
java8之Stream流
java·后端
白总Server2 小时前
JVM 处理多线程并发执行
jvm·后端·spring cloud·微服务·ribbon·架构·数据库架构
江梦寻3 小时前
解决SLF4J: Class path contains multiple SLF4J bindings问题
java·开发语言·spring boot·后端·spring·intellij-idea·idea
LightOfNight3 小时前
Redis设计与实现第9章 -- 数据库 总结(键空间 过期策略 过期键的影响)
数据库·redis·后端·缓存·中间件·架构