SpringCloud源码解析之eureka-client客户端


前言

分析eureka源码建议先从client端开始,再进入到server端

一、Eureka流程图

在这里插入图片描述
图片来源:https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance
在这里插入图片描述
图片来源:《Spring Cloud 微服务架构进阶》

从上图可以看出整个Eureka是分为client(客户端)、server(服务端),其中我要对图中的几个关键过程进行简单叙述;

1. 注册Register:客户端向服务端注册

读取自身服务实例配置信息,封装成EurekalnstanceConfig;
从Eureka Server中拉取注册信息表;

2. 续约Renew:客户端会定时(默认30s一次)向服务端发送自己的主机信息,并更新服务端列

3. 下线Cancel:服务下架

4. 获取注册表Get Registry:获取服务端中,客户端注册信息列表

5. 同步复制Replicate:Server集群之间同步注册信息列表

二、Eureka Client源码解析

1、源码入口

Eukeka Client 通过 Starter 的方式引人依赖, Spring Boot 将会为项目使用以下 自动配置类
在这里插入图片描述在这里插入图片描述

2、Client的配置类

2.1、进入EurekaClientAutoConfiguration类

在这里插入图片描述
重点关注两个注解:
@AutoConfigureBefore:表明在配置3个类之前,先要完成当前类配置才可以(当前类配置在前)
@AutoConfigureAfter:表示如果想让当前配置类起作用,需要先对3个配置类进行配置(当前类配置在后)

值得注意的是在2.1.1中@AutoConfigureAfter这里是3个类,DiscoveryClientOptionalArgsConfiguration是以@Import(DiscoveryClientOptionalArgsConfiguration.class)注入的

2.2、在EurekaClientAutoConfiguration类中初始化了一些关键Bean

    // 这个类的作用是将配置文件中以eureka.client为前缀的配置信息进行读取后封装
	@Bean
	@ConditionalOnMissingBean(value = EurekaClientConfig.class,
			search = SearchStrategy.CURRENT)
	public EurekaClientConfigBean eurekaClientConfigBean(ConfigurableEnvironment env) {
		// 前配置文件中以eureka.client为前缀的信息进行读取并封装
		return new EurekaClientConfigBean();
	}

    // 这个类的作用是将关于eureka实例的配置信息进行读取后封装。
	@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"));


2.3、重点关注EurekaClientAutoConfiguration类中的RefreshableEurekaClientConfiguration类

因为eureka的客户端是在这里创建的
在这里插入图片描述
DiscoveryClient Spring Cloud 中用来进行服务发现的顶级接口
在这里插入图片描述

3、从Eureka Server中拉取注册信息表

在这里插入图片描述
在这里插入图片描述
目标是进入DiscoveryClient中的下图方法中
在这里插入图片描述
这个方法很长,需要关注的是以下几点:
1、shouldRegisterWithEureka(对应配置eureka.client.register-with-eureka )为 true 表示Eureka Client 将注册到 Eureka Server;
2、shouldFetchRegistry(对应配置为 eureka client.fetch-register)为 true 表示 Eureka Client 将从 Eureka Server中拉取注册表信息;

如果上述的两个配置均为 false 那么 Discovery 的初始化时就直接结束,表示该客户端既不进行服务注册也不进行服务发现。
在这里插入图片描述
3、在初始化3个线程池(后续定时任务中会说)后,红框中的方法是去服务端拉取注册信息;
在这里插入图片描述
clientConfig.shouldFetchRegistry()上面有提,默认为true,接着进入到fetchRegistry(false)方法:
该方法中Applications在开篇中有解释;
在这里插入图片描述
进入getAndStoreFullRegistry()方法:
在这里插入图片描述
继续跟进,注意这里要进入AbstractJerseyEurekaHttpClient:
serviceUrl即为server地址;
从Server端拉取注册表使用的是Jersey中的GET请求;
并且在返回200之后,将 Eureka Server 中拉取注册表中所有的服务 例信息
全量拉取将 Eureka Server 中拉取注册表中所有的服务实例信息(封装在 Applications
中),并经过处理后替换掉本地注册表缓存Applications;
在这里插入图片描述
值得注意的是:getAndStoreFullRegistry()方法可能会被多个线程同时调用,导致新拉取的注册表被旧的注册表覆盖,产生了脏数据,因为Eureka通过Atomic的对applications的更新版本进行CAS更新,拉取到注册表信息之后对获取到的applications信息进行筛选,只保留状态为UP的服务实例信息

4、向Eureka Server中注册

再次回到DiscoveryClient中,在获取完注册表信息后:
clientConfig.shouldRegisterWithEureka()表示:是否注册到Eureka;
clientConfig.shouldEnforceRegistrationAtInit()表示:在初始化的时候强制进行注册;
在这里插入图片描述
进入register() 注册方法:
Eureka Client 会将自身服务实例元数据(封装在 Instancelnfo 中)发送到 Eureka Server 中请求服务注册,当 Eureka Server 返回 NO_CONTENT (204)状态码时,说明服务注册成功。
在这里插入图片描述
值得注意的是:这里请注册是PUT方法,并且在调用的是AbstractJerseyEurekaHttpClient的register时eureka使用了装饰器模式,提升了组件的扩展性:
在这里插入图片描述
在这里插入图片描述

JerseyClient:执行rest请求的工具,eureka有RestTemplate的替换实现方案
统计信息:根据请求类型进行统计,请求时间总计、请求失败次数总计等;
重定向:返回的http code是302则表示要重定向(注册中心迁移?)更换重定向的url再次发起请求,最多重定向10次;
失败重试机制:已经请求失败的server url记录下来下次不再请求;如果失败的url过多(比如全部都失败的情况,可能是客户端网络有问题),超过阀值,则清空失败的url记录,把他们重新当成可用的url ,这里要获取注册中心地址列表,用于重试;
会话:请求失败的url记录多久,永久记录吗?使用session机制如果20分钟没请求过,则重置上面的所有机制,session有效时长20分钟左右(有随机增减)

5、定时任务

  • 定时更新客户端注册表
    在server注册表中的服务实例信息是动态变化的,为了保持最新一致性,client需求定时从server上拉取注册表信息并更新本地缓存
  • 定时心跳续租
    服务注册应该是一个持续的过程,client通过定时发送心跳的方式与server进行通信,以维持自己在server注册表上的租约续期
  • 定时检查更新续约信息
    为了监控client上的信息和状态变化,client设置了一个注册定时器,定时检查应用信息或状态的变化,并在发生变化时向server重新注册,避免在注册表中的本服务实例信息不可用

5.1 定时任务:更新客户端注册表

可以通过过eureka.client.registry-fetch-interval-seconds进行设置,默认30s刷新一次
由TimedSupervisorTask提供实现,TimedSupervisorTask继承了TimerTask,执行定时任务
在这里插入图片描述
进入new CacheRefreshThread()中的refreshRegistry()方法
在这里插入图片描述
这里又回到了fetchRegistry(boolean forceFullRegistryFetch) 方法,这次走的增量拉取server端的注册表信息,上图代码后续就是说如果发现变化,就打印更新注册表之后的变化
在这里插入图片描述

5.2 定时任务:定时心跳续租

可以通过过eureka.instance.lease-renewal-interval-in-seconds进行设置,默认30s刷新一次
由TimedSupervisorTask提供实现,TimedSupervisorTask继承了TimerTask,执行定时任务
在这里插入图片描述
在这里插入图片描述
注意这里的Status.NOT_FOUND 指的是,发送心跳实际上是将instanceinfo发送到服务端,如果服务端的注册表信息中没有我的这个instanceinfo,就会发生NOT_FOUND,如果状态是NOT_FOUND ,则重新进行注册操作
在这里插入图片描述
进入sendHeartBeat()方法中,在向server发送心跳续约请求时,需要关注LastDirtyTimestamp,server接收到这个请求,会对这个字段数值进行比较,详细内容将在server端中做解释
在这里插入图片描述

5.3 定时任务:检查更新续约信息

InstanceInfoReplicator类的作用是:更新和复制本地的instanceinfo到远程server,这里要说明一点,client的配置文件是可以动态修改的,这里面会通过定时任务去检查配置文件的修改并同步到server端
在这里插入图片描述

1、使用了定时的线程池

在这里插入图片描述

2、start()后将执行run()方法

在这里插入图片描述

3、跟进discoveryClient.refreshInstanceInfo();方法中

在这里插入图片描述
refreshDataCenterInfoIfRequired()主要是为了检查hostname是否发生改变;
refreshLeaseInfoIfRequired(),是为了更新租约信息,重点更进:
1、instanceInfo.getLeaseInfo()是获取当前的续约信息;
2、getLeaseExpirationDurationInSeconds()是在配置文件eureka.instance.lease-renewal-interval-in-seconds:指定客户端多少秒向服务端发一次心跳;
3、getLeaseRenewalIntervalInSeconds()是在配置文件eureka.instance.lease-renewal-interval-in-seconds:指定当前客户端多少秒内没有向服务端发送心跳,则让服务端认为其宕机,踢除掉该服务;
4、instanceInfo.setIsDirty():这个字段代表instanceInfo配置已经被修改了,并记录最新修改的时间戳
在这里插入图片描述在这里插入图片描述

4、再次回到run()方法

isDirtyWithTime()方法中校验了isInstanceInfoDirty字段,如果在instanceInfo配置被修改了,在上图中就被修改为了true,就需要重新注册。
也可以说如果把续约信息改掉了,则说明这个instanceinfo对server来说是一个全新的,需要注册。
在这里插入图片描述在这里插入图片描述

5、检查更新续约信息流程图

在这里插入图片描述

6、服务下线

一般情况下,应用服务在关闭的时候, Eureka Client会主动向 Eureka Server注销自身在注册表中的信息。
源码入口:EurekaClientAutoConfiguration->RefreshableEurekaClientConfiguration
在这里插入图片描述
这一段代码在最一开始创建EurekaClient代码中有一个destroyMethod = “shutdown”,跟进这个方法:

1、首先执行cancelScheduledTasks() 取消所有定时任务;

2、依次停止了 定时检测更新配置文件、定时心跳、定时更新注册表 以及 其他定时任务;

3、修改instanceInfo状态为DOWN以及关闭监视器;

在这里插入图片描述
在这里插入图片描述

4、跟进unregister()方法,进行服务下架;

5、最终是发送delete请求完成服务下架;

在这里插入图片描述
服务下线的接口地址为 apps ${APP_NAME} ${INSTANCE_INFO_ID },传递参数为服
务名和服务实例 id, HTTP 方法为 delete

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值