本人小白一枚,第一次发文章,格式字体勿喷,我会进行改进的,我自己都看不下去了哈

一、Eureka体系架构

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

  1. Register:客户端向服务端注册过程
  2. Renew:心跳(续约),客户端会定时向服务端发送自己的主机信息,并更新服务端列表
  3. Get:获取服务端的客户端注册信息列表
  4. Cancel:服务下架
  5. Replicate:服务端之间同步注册信息列表

二、源码解析

1、从何下手?

我们都知道SpringCloud是基于SpringBoot的,而SpringBoot最大的特点就是自动配置,所以我们可以从Eureka Client的自动配置开始。
Eureka Client 源码解析
首先准备一个简单SpringCloud项目,从上图看是一个SpringBoot的启动主类,其中有几个注解我先说一下;

  1. @SpringBootApplication
  2. @EnableDiscoveryClient:启用服务发现,可以连接任意注册中心
  3. @EnableEurekaClient:启用Eureka客户端,只能连接Eureka Server

接下来我们跟进@SpringBootApplication源码可以看到如下图:
Eureka Client 源码解析
我们可以发现@SpringBootApplication是一个组合注解,其核心注解有两个分别是:

  1. @EnableAutoConfiguration
  2. @ComponentScan

其中注意@ComponentScan注解仅仅是配置扫描指令,而不做具体扫描工作,而@EnableAutoConfiguration这个注解最终是要完成如下两个工作;

  1. 扫描自定义包(开发者自己定义的base-packages),将扫描到到类交由Spring容器管理
  2. 扫描默认自动配置相关包(META-INF/spring.factories),将扫描到到类交由Spring容器管理
    Eureka Client 源码解析
    Eureka Client 源码解析

这里我们最应该从EurekaClientAutoConfiguration这个类开始进行分析
Eureka Client 源码解析

  1. @ConditionalOnClass(EurekaClientConfig.class):该注解是一个条件注解,表明在类路径下需要有EurekaClientConfig这个接口的实现,其定义了eureka client向eureka server注册的必须配置信息,默认实现是DefaultEurekaClientConfig类。

  2. @Import(DiscoveryClientOptionalArgsConfiguration.class):该注解的作用是导入DiscoveryClientOptionalArgsConfiguration这个类。

  3. @ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class):该注解是一个条件注解,表明当前Spring容器中需要有Marker这个类的实例。

  4. @ConditionalOnProperty(value = “eureka.client.enabled”, matchIfMissing = true):该注解表示eureka.client.enabled这个属性如果没有配置默认值就是true。
    @ConditionalOnDiscoveryEnabled:如下图所示,表示spring.cloud.discovery.enabled这个属性如果没有配置默认值就是true。
    Eureka Client 源码解析

  5. @AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,
    CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class }):表明在配置
    NoopDiscoveryClientAutoConfiguration、CommonsClientAutoConfiguration、ServiceRegistryAutoConfiguration类之前,先要完成当前类配置才可以(当前类配置在前)

  6. @AutoConfigureAfter(name = {
    “org.springframework.cloud.autoconfigure.RefreshAutoConfiguration”,
    “org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration”,
    “org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration” }):表示如果想让当前配置类起作用,需要先对RefreshAutoConfiguration、EurekaDiscoveryClientConfiguration、AutoServiceRegistrationAutoConfiguration这些配置类进行配置(当前类配置在后)
    注意:我们现在先关注一下EurekaDiscoveryClientConfiguration这个类
    Eureka Client 源码解析
    首先这是一个配置类(带有@Configuration注解的类),其次这里有一个@Bean刚好是创建了一个Marker类的实例,并将其纳入到Spring容器。这正好对应了之前看到的@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)

再回到EurekaClientAutoConfiguration类中,其还创建了一些重要的bean,比如:
Eureka Client 源码解析
Eureka Client 源码解析
这个类的作用是将配置文件中以eureka.client为前缀的配置信息进行读取后封装。
再有:
Eureka Client 源码解析
这个类的作用是将关于eureka实例的配置信息进行读取后封装。
接下来继续往下看,会发现有一个内部类
Eureka Client 源码解析
我们最终eureka的客户端是在这里创建的。所以说对于eureka client的源码应该从下图框框处开始跟
Eureka Client 源码解析
Eureka Client 源码解析
现在我们已经找到了入口,下面我会补充一些预备知识,为接下来阅读源码打基础。

2、预备知识

1. InstanceInfo类
Eureka Client 源码解析
该类的作用是,持有了client向server注册必须的信息和被其他组件发现,也就是说这个InstanceInfo代表当前主机的信息,注册实际上是注册InstanceInfo,而服务发现,也就是发现InstanceInfo。
2. Application
Eureka Client 源码解析
在这个Application中维护一个列表,如下图所示
Eureka Client 源码解析
也就是说Application中维护着某一个微服务名称下面所有的提供者信息的列表。我们可以把Application简单当作一个Set集合。
3. Applications
Eureka Client 源码解析
Eureka Client 源码解析
Applications就是Eureka Client中存放的
客户端注册表
注意这里说的是客户端注册表,而非服务端注册表(服务端注册表是一个Map<微服务名称,Application>)。
Eureka Client 源码解析
4. Jersey框架
该框架的功能与 SpringMVC 的相同,都是通过让用户提交 URI,然后处理器进行路由找到相应的后台业务。这个处理器不叫 Controller,而叫 Resource。Eureka Client 与Eureka Server 间,及 Eureka Server 间的通信是通过 Jersey 来完成的。也就是说在文章开始我们说的一些重要过程中Register应该提交的是post请求,Cancel提交delete请求,Renew提交put请求,Get提交get请求。

3、客户端注册表的获取

Eureka Client 源码解析
首先跟进super()
Eureka Client 源码解析
通过this()调用了构造方法,传进了4个参数,我们直接看第四个参数,其是以匿名内部类的方式传递的。
Eureka Client 源码解析
类中有一个get()方法,其获取的是BackupRegistry(备用的注册中心),那什么是备用的注册中心呢?由于Eureka是基于AP模型的,client每30秒会从server下载注册表信息,当client连接不上集群中任意一台server时,将直接采用本地注册列表来保证整体服务可用。这也就是本地注册中心或者说是备用注册中心。下面继续
Eureka Client 源码解析
跟进this()
Eureka Client 源码解析
注意这个方法非常长,主要大部分代码其实在做一些准备工作,我们主要关心如下代码
Eureka Client 源码解析
首先看clientConfig.shouldFetchRegistry() 这里面clientConfig指的是客户端配置信息,shouldFetchRegistry()这个方法对应的配置项是fetch-registry此配置项默认为true
Eureka Client 源码解析
fetchRegistry(false)这个方法的作用就是获取注册表,返回值是boolean(表示获取注册表成功或失败),方法参数表示是否强制获取所有注册表,也就是说如果if条件满足,则需要获取本地注册表。
继续跟进fetchRegistry(false)
Eureka Client 源码解析
Applications是啥?前面说过了
Eureka Client 源码解析
Eureka Client 源码解析
注意:由于client每30秒去server端下载客户端注册表,当前是服务第一次启动所以获取到的applications肯定是null。
Eureka Client 源码解析此时if条件是满足的,故肯定会执行if语句里面的getAndStoreFullRegistry(),获取和存储所有注册信息。
Eureka Client 源码解析
跟进getAndStoreFullRegistry()
Eureka Client 源码解析
注意这个方法:clientConfig.getRegistryRefreshSingleVipAddress(),clientConfig为客户端配置信息,其在配置文件中对应的是:
Eureka Client 源码解析
这个配置项的作用是:由于defaultZone这个是可以配置多台server的(集群),如果配置了registry-refresh-single-vip-address,则认定某一台为主节点,这时client连接时会只连接这个主节点,而不配置registry-refresh-single-vip-address,则说明client连接时会将所有的server进行shuffle(打乱)后随机连接一台。
这里由于没有配置该配置项,故跟进
Eureka Client 源码解析
注意这里要进入AbstractJerseyEurekaHttpClient
Eureka Client 源码解析
Eureka Client 源码解析
继续跟进
Eureka Client 源码解析
Eureka Client 源码解析
通过response = requestBuilder.accept(MediaType.APPLICATION_JSON_TYPE).get(ClientResponse.class);这行代码可以清楚看到,提交的是get请求,而serviceUrl则是server地址。
Eureka Client 源码解析

4、客户端注册

继续往下,找到注册
Eureka Client 源码解析
跟进register()方法,由于之前分析register()应该发送一个post请求,而post请求发送的是InstanceInfo对象,也就是将主机信息注册到server端
Eureka Client 源码解析
继续跟进
Eureka Client 源码解析
Eureka Client 源码解析
Eureka Client 源码解析
最终根据响应的状态码来决定注册是否成功
Eureka Client 源码解析

5、定时任务

继续往下
Eureka Client 源码解析
initScheduledTasks():启动一堆定时任务

(1) 第一个定时任务

第一个定时任务就是定时更新客户端注册表Eureka Client 源码解析
clientConfig.getRegistryFetchIntervalSeconds()对应配置文件中的配置,指的是每30秒从server端下载注册表信息(默认30秒)
Eureka Client 源码解析
从下图可以看出定时任务的时间以及具体的任务
Eureka Client 源码解析
跟进new CacheRefreshThread(),可以看到该类是一个线程类且运行run()方法
Eureka Client 源码解析
跟进refreshRegistry()方法
Eureka Client 源码解析
这一段代码指的是从远程地区获取注册中心,其对应的配置如下
Eureka Client 源码解析
这一块代码不做详细描述,继续往下
Eureka Client 源码解析
remoteRegionsModified代表前一段代码中的远程region是否被修改了,我们这里没有配置则说明是false,跟进fetchRegistry()方法
Eureka Client 源码解析
注意,由于此时的applications不为null了,if条件不成立,则会走else分支,跟进
getAndUpdateDelta(applications),也就是获取并修改注册表信息
Eureka Client 源码解析
delta在这里面的意思被翻译成最新的,由于这个定时任务是每30秒获取注册表信息,所以跟进getDelta()方法,这里面依然是发送get请求去获取。
Eureka Client 源码解析
Eureka Client 源码解析
如果delta为空,意味着没有获取到最新的,没有获取到最新的我就必须再全部获取一次,如果delta不为空,则需要更新,跟进updateDelta()方法
Eureka Client 源码解析
这里面代码也比较长,主要用作更新操作(把delta中applications下的instanceinfo取出来与本地的做比较),详细逻辑这里不做分析

(2) 第二个定时任务

第二个定时任务就是定时心跳
Eureka Client 源码解析
getLeaseInfo():指的是获取续约信息(Lease:续约)
getRenewalIntervalInSecs():在配置文件中的配置是
Eureka Client 源码解析
跟进new HeartbeatThread()
Eureka Client 源码解析
renew()方法返回值是boolean,心跳成功则记录最后成功方法心跳的时间戳,继续跟进renew()方法Eureka Client 源码解析
跟进sendHeartBeat()方法
Eureka Client 源码解析
注意这里提交的是put请求,是更新操作,但是put请求具体携带的数据我们在这里面没有看到,等下次将服务端源码解析时会看到
Eureka Client 源码解析
注意这里的Status.NOT_FOUND 指的是,发送心跳实际上是将instanceinfo发送到服务端,如果服务端的注册表信息中没有我的这个instanceinfo,就会发生NOT_FOUND
Eureka Client 源码解析
如果状态是NOT_FOUND ,则重新进行注册操作。

(3) 第三个定时任务

第三个定时任务就是定时检查更新续约信息
Eureka Client 源码解析
其中跟进new InstanceInfoReplicator(),发现其是一个线程类
Eureka Client 源码解析
该类的作用是:更新和复制本地的instanceinfo到远程server,这里要说明一点,client的配置文件是可以动态修改的,这里面会通过定时任务去检查配置文件的修改并同步到server端
该线程在这里(还是在定时任务的方法里面)启动的Eureka Client 源码解析
启动后将执行run()方法
Eureka Client 源码解析
首先跟进refreshInstanceInfo()方法
Eureka Client 源码解析
这里主要关心refreshLeaseInfoIfRequired()方法,更新续约信息,跟进
Eureka Client 源码解析
其中instanceInfo.getLeaseInfo()是获取当前的续约信息
Eureka Client 源码解析
这些是获取配置文件中的续约相关信息,其中config.getLeaseExpirationDurationInSeconds()在配置文件中是
Eureka Client 源码解析
而后进行比较(当前与配置文件中)
Eureka Client 源码解析
Eureka Client 源码解析
这一块代表instanceinfo配置已经被修改了,并记录最新修改的时间戳
Eureka Client 源码解析
接下来继续返回到run()方法里
Eureka Client 源码解析
跟进isDirtyWithTime()方法
Eureka Client 源码解析
这里面的isInstanceInfoDirty指的是上述是否修改配置文件信息的操作,如果修改了返回最新修改时间戳否则返回null,继续向下
Eureka Client 源码解析
如果dirtyTimestamp不为null说明配置文件修改了,则重新执行注册操作。也就是说如果你把续约信息改掉了,则说明这个instanceinfo对server来说是一个全新的,需要注册。

6、服务下架

Eureka Client 源码解析
这一段代码在最一开始创建EurekaClient代码中有一个destroyMethod = “shutdown”,跟进这个方法Eureka Client 源码解析
首先执行cancelScheduledTasks() 取消所有定时任务
Eureka Client 源码解析
依次停止了定时检测更新配置文件、定时心跳、定时更新注册表以及其他定时任务。
Eureka Client 源码解析
Eureka Client 源码解析
修改instanceinfo状态为DOWN以及关闭监视器
Eureka Client 源码解析
跟进unregister()方法,进行服务下架
Eureka Client 源码解析
跟进cancel()方法
Eureka Client 源码解析
最终是发送delete请求完成服务下架。

三、总结

整体跟源码结构大致如下图
Eureka Client 源码解析

相关文章: