🚀 优质资源分享 🚀
学习路线指引(点击解锁) | 知识定位 | 人群定位 |
---|---|---|
🧡 Python实战微信订餐小程序 🧡 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
💛Python量化交易实战💛 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
大家好,本文我将继续来剖析SpringCloud中负载均衡组件Ribbon的源码。本来我是打算接着OpenFeign动态代理生成文章直接讲Feign是如何整合Ribbon的,但是文章写了一半发现,如果不把Ribbon好好讲清楚,那么有些Ribbon的细节理解起来就很困难,所以我还是打算单独写一篇文章来剖析Ribbon的源码,这样在讲Feign整合Ribbon的时候,我就不再赘述这些细节了。好了,话不多说,直接进入主题。
一、Ribbon的核心组件
1、Server
这是个很简单的东西,就是服务实例数据的封装,里面封装了服务实例的ip和端口之类的,一个服务有很多台机器,那就有很多个Server对象。
2、ServerList
| 123456789101112 | public
interface
ServerList
extends
Server> {
`public` `List getInitialListOfServers();` `/**
* Return updated list of servers. This is called say every 30 secs* (configurable) by the Loadbalancer's Ping cycle
/``public
List getUpdatedListOfServers();
}` |
ServerList是个接口,泛型是Server,提供了两个方法,都是获取服务实例列表的,这两个方法其实在很多实现类中实现是一样的,没什么区别。这个接口很重要,因为这个接口就是Ribbon获取服务数据的来源接口,Ribbon进行负载均衡的服务列表就是通过这个接口来的,那么可以想一想是不是只要实现这个接口就可以给Ribbon提供服务数据了?事实的确如此,在SpringCloud中,eureka、nacos等注册中心都实现了这个接口,都将注册中心的服务实例数据提供给Ribbon,供Ribbon来进行负载均衡。
3、ServerListUpdater
通过名字也可以知道,是用来更新服务注册表的数据,他有唯一的实现,就是PollingServerListUpdater,这个类有一个核心的方法,就是start,我们来看一下start的实现。
| 12345678910111213141516171819202122232425262728293031 | @Override``public
synchronized
void
start(``final
UpdateAction updateAction) {``if
(isActive.compareAndSet(``false``,
true``)) {``final
Runnable wrapperRunnable =
new
Runnable() {``@Override``public
void
run() {``if
(!isActive.get()) {``if
(scheduledFuture !=
null``) {``scheduledFuture.cancel(``true``);``}``return``;``}``try
{``updateAction.doUpdate();``lastUpdated = System.currentTimeMillis();``}
catch
(Exception e) {``logger.warn(``"Failed one update cycle"``, e);``}``}``};
scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(``wrapperRunnable,``initialDelayMs,``refreshIntervalMs,``TimeUnit.MILLISECONDS``);``}
else
{``logger.info(``"Already active, no-op"``);``}``}
|
通过这段方法我们可以看出,首先通过isActive.compareAndSet(false, true)来保证这个方法只会被调用一下,然后封装了一个Runnable,这个Runnable干了一件核心的事,就是调用传入的updateAction的doUpdate方法,然后将Runnable扔到了带定时调度功能的线程池,经过initialDelayMs(默认1s)时间后,会调用一次,之后都是每隔refreshIntervalMs(默认30s)调用一次Runnable的run方法,也就是调用updateAction的doUpdate方法。
所以这个类的核心作用就是每隔30s会调用一次传入的updateAction的doUpdate方法的实现,记住这个结论。
4、IRule
| 123456789101112131415 | public
interface
IRule{``/*``* choose one alive server from lb.allServers or``* lb.upServers according to key``*
* @return choosen Server object. NULL is returned if none``* server is available
*/
public
Server choose(Object key);
public
void
setLoadBalancer(ILoadBalancer lb);
public
ILoadBalancer getLoadBalancer();
}
|
IRule是负责负载均衡的算法的,也就是真正实现负载均衡获取一个服务实例就是这个接口的实现。比如说实现类RandomRule,就是从一堆服务实例中随机选取一个服务实例。
5、IClientConfig
就是一个配置接口,有个默认的实现DefaultClientConfigImpl,通过这个可以获取到一些配置Ribbon的一些配置。
6、ILoadBalancer
| 123456789101112131415 | public
interface
ILoadBalancer {
public
void
addServers(List newServers);
public
Server chooseServer(Object key);
public
void
markServerDown(Server server);
@Deprecated``public
List getServerList(``boolean
availableOnly);
public
List getReachableServers();
public
List getAllServers();``}
|
这个接口的作用,对外主要提供了获取服务实例列表和选择服务实例的功能。虽然对外主要提供获取服务的功能,但是在实现的时候,主要是用来协调上面提到的各个核心组件的,使得他们能够协调工作,从而实现对外提供获取服务实例的功能。
这个接口的实现有好几个实现类,但是我讲两个比较重要的。
BaseLoadBalancer
| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 | public
class
BaseLoadBalancer
extends
AbstractLoadBalancer
implements``PrimeConnections.PrimeConnectionListener, IClientConfigAware {
private
final
static
IRule DEFAULT_RULE =
new
RoundRobinRule();
protected
IRule rule = DEFAULT_RULE;``private
IClientConfig config;
protected
volatile
List allServerList = Collections``.synchronizedList(``new
ArrayList());``protected
volatile
List upServ