springcloud源码文档,springcloud api

http://www.itjxue.com  2023-01-15 04:53  来源:未知  点击次数: 

Spring Cloud之Eureka源码分析2

本章主要介绍Eureka Client端源码分析。

客户端主要是向Server服务端发送Http请求,主要有注册,心跳续约,获取注册信息等功能

在分析源码之前,需要查看下客户端配置文件

application.yml

访问服务端localhost:8000的注册信息

自动配置类,首先要从依赖包的spring.factories文件看起

其中最重要的是EurekaClientAutoConfiguration类

只要类中存在EurekaClientConfig类所在的依赖包eureka-client-xx.jar就可以加载这个类

2、RefreshableEurekaClientConfiguration 用来开启定时任务

1、EurekaAutoServiceRegistration

实例化EurekaAutoServiceRegistration对象,并放到spring容器中

org.springframework.cloud.netflix.eureka.serviceregistry.EurekaAutoServiceRegistration

2、start

由于EurekaAutoServiceRegistration类实现了SmartLifecycle,SmartApplicationListener等接口,所以会在容器初始化完成之后调用EurekaAutoServiceRegistration#start方法。

调用ApplicationInfoManager应用信息管理设置实例初始化状态信息initialStatus

4、setInstanceStatus

com.netflix.appinfo.ApplicationInfoManager#setInstanceStatus

设置实例状态信息并调用监听器notify方法

com.netflix.discovery.DiscoveryClient#register

这里就到了注册客户端的地方

包括缓存更新与心跳续约,通过RefreshableEurekaClientConfiguration类开始初始化,并最终通过initScheduledTasks方法开启定时调度器任务

EurekaClientAutoConfiguration.RefreshableEurekaClientConfiguration

这个类是程序实现定时刷新任务开始的地方,主要是通过new CloudEurekaClient()方法创建Cloud客户端类(CloudEurekaClient)

下面主要查看CloudEurekaClient的调用链

1、CloudEurekaClient#CloudEurekaClient

org.springframework.cloud.netflix.eureka.CloudEurekaClient#CloudEurekaClient

调用CloudEurekaClient类的父类,和对applicationInfoManager、publisher、eurekaTransportField等属性值赋值

2、DiscoveryClient#DiscoveryClient

com.netflix.discovery.DiscoveryClient#DiscoveryClient

4、DiscoveryClient#DiscoveryClient

定义调度器类、心跳执行器和缓存刷新执行器等的定义。

com.netflix.discovery.DiscoveryClient#DiscoveryClient

这个方法主要流程:

①、这个方法前半部分是初始化属性值。

②、根据客户端client配置文件,config.shouldFetchRegistry()是否获取注册表信息和

config.shouldRegisterWithEureka()是否注册到eureka上来对属性赋值,或直接返回

③、初始化调度器scheduler、两个线程池执行器heartbeatExecutor(心跳续约)和cacheRefreshExecutor(缓存刷新,定时获取注册信息表)

④、在获取服务注册信息条件下,没有获取到信息或异常即fetchRegistry(false)返回false。可以从备用服务器获取调用fetchRegistryFromBackup()方法,内部实现方法调用备用服务器类的get方法backupRegistryProvider.get()

⑤、初始化调度器任务方法initScheduledTasks()

调度器任务包括:

1、定时刷新缓存注册表信息,分为全量获取和增量获取

2、定时向服务端发送心跳续约

3、状态改变监听器执行

这里不仅包括这些定时任务,注册也是在这里调用状态改变监听器StatusChangeListener的notify方法

com.netflix.discovery.DiscoveryClient#initScheduledTasks

1、initScheduledTasks

com.netflix.discovery.DiscoveryClient#initScheduledTasks

TimedSupervisorTask继承了TimerTask,TimerTask实现了Runnable

TimedSupervisorTask类的构造方法

2、HeartbeatThread

执行TimedSupervisorTask的task任务,在给定的间隔内执行心跳续约任务

com.netflix.discovery.DiscoveryClient.HeartbeatThread

3、renew

续约任务,续约成功更新lastSuccessfulHeartbeatTimestamp参数。通过REST方式进行续订

com.netflix.discovery.DiscoveryClient#renew

服务端调用到renewLease方法续约,appName和id与客户端传过来的相同

com.netflix.eureka.resources.InstanceResource#renewLease

在定时刷新缓存实现获取注册信息,分为全量拉取和增量拉取

创建TimedSupervisorTask调度任务类,传入cacheRefreshExecutor执行器、CacheRefreshThread任务类、从服务端获取注册信息的时间间隔RegistryFetchIntervalSeconds等参数信息

全量拉取条件(任意一个)

①、disable-delta属性值是true 关闭增量拉取

②、registry-refresh-single-vip-address 属性vip地址的值不为空

③、forceFullRegistryFetch 为true 传过来的变量值

④、localRegionApps的applications是null 当前区域应用

⑤、applications的数量是0

⑥、applications的版本是-1

实现增量拉取的条件是不符合全量拉取,调用getAndUpdateDelta方法

com.netflix.discovery.DiscoveryClient#getAndUpdateDelta

这个方法实现了增量拉取的请求实现,及对拉取增量结果的处理

1、getDelta

eurekaTransport.queryClient.getDelta(remoteRegionsRef.get())的具体实现是通过AbstractJerseyEurekaHttpClient类实现的

com.netflix.discovery.shared.transport.jersey.AbstractJerseyEurekaHttpClient#getDelta

这个方法主要是遍历recentlyChangedQueue存在的数据放入到Applications对象中。所以recentlyChangedQueue队列中存在什么数据就很重要,因此我们需要了解最新更新队列recentlyChangedQueue是如何放入的及放入那些数据,及其的移除的原理。

在这个方法最后 apps.setAppsHashCode设置了当前服务端所有注册信息的HashCode,所以这个增量对象存储了最新的状态HashCode值。

7、客户端获取增量数据的处理

还是在getAndUpdateDelta方法内,对服务端传输过来数据,获取当前服务端的增量数据部分

com.netflix.discovery.DiscoveryClient#getAndUpdateDelta

这个方法的主要过程是:

如果增量数据部分为空,则执行全量拉取。

对当前服务的注册信息表执行updateDelta(delta)方法,对当前注册实例的增加删除或修改操作

当前更新后的服务注册表的HashCode值与增量对象存储的最新的状态HashCode值比较,如果不相等 则执行全量拉取

最新更新队列ConcurrentLinkedQueueRecentlyChangedItem recentlyChangedQueue

com.netflix.eureka.registry.AbstractInstanceRegistry#AbstractInstanceRegistry类在构建创建注册表时创建了recentlyChangedQueue队列,并创建了一个增量调度任务方法getDeltaRetentionTask方法

com.netflix.eureka.registry.AbstractInstanceRegistry#getDeltaRetentionTask

对recentlyChangedQueue队列中对最近改变的队列在一定时间范围retentionTimeInMSInDeltaQueue=180000ms(3分钟)外的进行定时清除(30s清除一次)

3、statusUpdate

4、deleteStatusOverride

getDeltaRetentionTask进行定时清除

全量拉取与增量拉取过程类似

全量拉取调用getAndStoreFullRegistry方法

1、getAndStoreFullRegistry

com.netflix.discovery.DiscoveryClient#getAndStoreFullRegistry

2、getApplications

com.netflix.discovery.shared.transport.EurekaHttpClient#getApplications

「SpringCloud原理」Ribbon核心组件以及运行原理万字源码剖析

大家好,本文我将继续来剖析SpringCloud中负载均衡组件Ribbon的源码。本来我是打算接着OpenFeign动态代理生成文章直接讲Feign是如何整合Ribbon的,但是文章写了一半发现,如果不把Ribbon好好讲清楚,那么有些Ribbon的细节理解起来就很困难,所以我还是打算单独写一篇文章来剖析Ribbon的源码,这样在讲Feign整合Ribbon的时候,我就不再赘述这些细节了。好了,话不多说,直接进入主题。

这是个很简单的东西,就是服务实例数据的封装,里面封装了服务实例的ip和端口之类的,一个服务有很多台机器,那就有很多个Server对象。

ServerList是个接口,泛型是Server,提供了两个方法,都是获取服务实例列表的,这两个方法其实在很多实现类中实现是一样的,没什么区别。这个接口很重要,因为这个接口就是Ribbon获取服务数据的来源接口,Ribbon进行负载均衡的服务列表就是通过这个接口来的,那么可以想一想是不是只要实现这个接口就可以给Ribbon提供服务数据了?事实的确如此,在SpringCloud中,eureka、nacos等注册中心都实现了这个接口,都将注册中心的服务实例数据提供给Ribbon,供Ribbon来进行负载均衡。

通过名字也可以知道,是用来更新服务注册表的数据,他有唯一的实现,就是PollingServerListUpdater,这个类有一个核心的方法,就是start,我们来看一下start的实现。

通过这段方法我们可以看出,首先通过isActive.compareAndSet(false, true)来保证这个方法只会被调用一下,然后封装了一个Runnable,这个Runnable干了一件核心的事,就是调用传入的updateAction的doUpdate方法,然后将Runnable扔到了带定时调度功能的线程池,经过initialDelayMs(默认1s)时间后,会调用一次,之后都是每隔refreshIntervalMs(默认30s)调用一次Runnable的run方法,也就是调用updateAction的doUpdate方法。

所以这个类的核心作用就是每隔30s会调用一次传入的updateAction的doUpdate方法的实现,记住这个结论。

IRule是负责负载均衡的算法的,也就是真正实现负载均衡获取一个服务实例就是这个接口的实现。比如说实现类RandomRule,就是从一堆服务实例中随机选取一个服务实例。

就是一个配置接口,有个默认的实现DefaultClientConfigImpl,通过这个可以获取到一些配置Ribbon的一些配置。

这个接口的作用,对外主要提供了获取服务实例列表和选择服务实例的功能。虽然对外主要提供获取服务的功能,但是在实现的时候,主要是用来协调上面提到的各个核心组件的,使得他们能够协调工作,从而实现对外提供获取服务实例的功能。

这个接口的实现有好几个实现类,但是我讲两个比较重要的。

BaseLoadBalancer

核心属性

allServerList:缓存了所有的服务实例数据

upServerList:缓存了能够使用的服务实例数据。

rule:负载均衡算法组件,默认是RoundRobinRule

核心方法

setRule:这个方法是设置负载均衡算法的,并将当前这个ILoadBalancer对象设置给IRule,从这可以得出一个结论,IRule进行负载均衡的服务实例列表是通过ILoadBalancer获取的,也就是 IRule 和 ILoadBalancer相互引用。setRule(rule)一般是在构造对象的时候会调用。

chooseServer:就是选择一个服务实例,是委派给IRule的choose方法来实现服务实例的选择。

BaseLoadBalancer这个实现类总体来说,已经实现了ILoadBalancer的功能的,所以这个已经基本满足使用了。

说完BaseLoadBalancer这个实现类,接下来说一下DynamicServerListLoadBalancer实现类。DynamicServerListLoadBalancer继承自BaseLoadBalancer,DynamicServerListLoadBalancer主要是对BaseLoadBalancer功能进行扩展。

DynamicServerListLoadBalancer

成员变量

serverListImpl:上面说过,通过这个接口获取服务列表

filter:起到过滤的作用,一般不care

updateAction:是个匿名内部类,实现了doUpdate方法,会调用updateListOfServers方法

serverListUpdater:上面说到过,默认就是唯一的实现类PollingServerListUpdater,也就是每个30s就会调用传入的updateAction的doUpdate方法。

这不是巧了么,serverListUpdater的start方法需要一个updateAction,刚刚好成员变量有个updateAction的匿名内部类的实现,所以serverListUpdater的start方法传入的updateAction的实现其实就是这个匿名内部类。

那么哪里调用了serverListUpdater的start方法传入了updateAction呢?是在构造的时候调用的,具体的调用链路是调用 restOfInit - enableAndInitLearnNewServersFeature(),这里就不贴源码了

所以,其实DynamicServerListLoadBalancer在构造完成之后,默认每隔30s中,就会调用updateAction的匿名内部类的doUpdate方法,从而会调用updateListOfServers。所以我们来看一看 updateListOfServers 方法干了什么。

这个方法实现很简单,就是通过调用 ServerList 的getUpdatedListOfServers获取到一批服务实例数据,然后过滤一下,最后调用updateAllServerList方法,进入updateAllServerList方法。

其实很简单,就是调用每个服务实例的setAlive方法,将isAliveFlag设置成true,然后调用setServersList。setServersList这个方法的主要作用是将服务实例更新到内部的缓存中,也就是上面提到的allServerList和upServerList,这里就不贴源码了。

其实分析完updateListOfServers方法之后,再结合上面源码的分析,我们可以清楚的得出一个结论,那就是默认每隔30s都会重新通过ServerList组件获取到服务实例数据,然后更新到BaseLoadBalancer缓存中,IRule的负载均衡所需的服务实例数据,就是这个内部缓存。

从DynamicServerListLoadBalancer的命名也可以看出,他相对于父类BaseLoadBalancer而言,提供了动态更新内部服务实例列表的功能。

为了便于大家记忆,我画一张图来描述这些组件的关系以及是如何运作的。

说完一些核心的组件,以及他们跟ILoadBalancer的关系之后,接下来就来分析一下,ILoadBalancer是在ribbon中是如何使用的。

ILoadBalancer是一个可以获取到服务实例数据的组件,那么服务实例跟什么有关,那么肯定是跟请求有关,所以在Ribbon中有这么一个抽象类,AbstractLoadBalancerAwareClient,这个是用来执行请求的,我们来看一下这个类的构造。

通过上面可以看出,在构造的时候需要传入一个ILoadBalancer。

AbstractLoadBalancerAwareClient中有一个方法executeWithLoadBalancer,这个是用来执行传入的请求,以负载均衡的方式。

这个方法构建了一个LoadBalancerCommand,随后调用了submit方法,传入了一个匿名内部类,这个匿名内部类中有这么一行代码很重要。

这行代码是根据给定的一个Server重构了URI,这是什么意思呢?举个例子,在OpenFeign那一篇文章我说过,会根据服务名拼接出类似 http:// ServerA 的地址,那时是没有服务器的ip地址的,只有服务名,假设请求的地址是 http:// ServerA/api/sayHello ,那么reconstructURIWithServer干的一件事就是将ServerA服务名替换成真正的服务所在的机器的ip和端口,假设ServerA所在的一台机器(Server里面封装了某台机器的ip和端口)是192.168.1.101:8088,那么重构后的地址就变成 http:// 192.168.1.101:8088/api/ sayHello ,这样就能发送http请求到ServerA服务所对应的一台服务器了。

之后根据新的地址,调用这个类中的execute方法来执行请求,execute方法是个抽象方法,也就是交给子类实现,子类就可以通过实现这个方法,来发送http请求,实现rpc调用。

那么这台Server是从获取的呢?其实猜猜也知道,肯定是通过ILoadBalancer获取的,因为submit方法比较长,这里我直接贴出submit方法中核心的一部分代码

就是通过selectServer来选择一个Server的,selectServer我就不翻源码了,其实最终还是调用ILoadBalancer的方法chooseServer方法来获取一个服务,之后就会调用上面的说的匿名内部类的方法,重构URI,然后再交由子类的execut方法来实现发送http请求。

所以,通过对AbstractLoadBalancerAwareClient的executeWithLoadBalancer方法,我们可以知道,这个抽象类的主要作用就是通过负载均衡算法,找到一个合适的Server,然后将你传入的请求路径 http:// ServerA/api/sayHello 重新构建成类似 http:// 192.168.1.101:8088/api/ sayHello 这样,之后调用子类实现的execut方法,来发送http请求,就是这么简单。

到这里其实Ribbon核心组件和执行原理我就已经说的差不多了,再来画一张图总结一下

说完了Ribbon的一些核心组件和执行原理之后,我们再来看一下在SpringCloud环境下,这些组件到底是用的哪些实现,毕竟有写时接口,有的是抽象类。

Ribbon的自动装配类:RibbonAutoConfiguration,我拎出了核心的源码

RibbonAutoConfiguration配置类上有个@RibbonClients注解,接下来讲解一下这个注解的作用

SpringClientFactory是不是感觉跟OpenFeign中的FeignContext很像,其实两个的作用是一样的,SpringClientFactory也继承了NamedContextFactory,实现了配置隔离,同时也在构造方法中传入了每个容器默认的配置类RibbonClientConfiguration。至于什么是配置隔离,我在OpenFeign那篇文章说过,不清楚的小伙伴可以后台回复feign01即可获得文章链接。

配置优先级问题

优先级最低的就是FeignContext和SpringClientFactory构造时传入的配置类

至于优先级怎么来的,其实是在NamedContextFactory中createContext方法中构建AnnotationConfigApplicationContext时按照配置的优先级一个一个传进去的。

RibbonClientConfiguration提供的默认的bean

接下来我们看一下RibbonClientConfiguration都提供了哪些默认的bean

配置类对应的bean,这里设置了ConnectTimeout和ReadTimeout都是1s中。

IRule,默认是ZoneAvoidanceRule,这个Rule带有过滤的功能,过滤哪些不可用的分区的服务(这个过滤可以不用care),过滤成功之后,继续采用线性轮询的方式从过滤结果中选择一个出来。至于这个propertiesFactory,可以不用管,这个是默认读配置文件的中的配置,一般不设置,后面看到都不用care。

至于为什么容器选择NacosServerList而不是ConfigurationBasedServerList,主要是因为NacosRibbonClientConfiguration这个配置类是通过@RibbonClients导入的,也就是比SpringClientFactory导入的RibbonClientConfiguration配置类优先级高。

ServerListUpdater,就是我们剖析的PollingServerListUpdater,默认30s更新一次BaseLoadBalancer内部服务的缓存。

那么在springcloud中,上图就可以加上注册中心。

三、总结

本文剖析了Ribbon这个负载均衡组件中的一些核心组件的源码,并且将这些组件之间的关系一一描述清楚,同时也剖析了在发送请求的时候是如何通过ILoadBalancer获取到一个服务实例,重构URI的过程。希望本篇文章能够让你知道Ribbon是如何工作的。

微服务架构 | *2.3 Spring Cloud 启动及加载配置文件源码分析(以 Nacos 为例)

参考资料 :

《Spring Microservices in Action》

《Spring Cloud Alibaba 微服务原理与实战》

《B站 尚硅谷 SpringCloud 框架开发教程 周阳》

Spring Cloud 要实现统一配置管理,需要解决两个问题:如何获取远程服务器配置和如何动态更新配置;在这之前,我们先要知道 Spring Cloud 什么时候给我们加载配置文件;

(责任编辑:IT教学网)

更多

推荐Mail服务器文章