Ribbon源码解析

Ribbon源码解析

首先,跟踪LoadBalancerClient的源码,它是一个接口类,继承了ServiceInstanceChooser,它的实现类为RibbonLoadBalancClient,它们之间的关系如下图:

在这里插入图片描述

LoadBalancerClient是一个负载均衡的客户端,有如下3种方法。其中有2个excute(方法,均用来执行请求,reconstructURI()用于 重构Url,代码如下:

在这里插入图片描述

ServiceInstanceChooser接口有一个方法根据serviceId获取ServiceInstance,即通过服务名来选择服务实例:

在这里插入图片描述

LoadBalancerClient的实现类为RibbonLoadBalancerClient。RibbonLoadBalancerClient是一个非常重要的类,最终的负载均衡的请求处理由它来执行:
在这里插入图片描述
在这里插入图片描述

在RibbonLoadBalancerClient的源码中,choose()方法用于选择具体服务实例。该方法通过getServer()方法去获取实例,经过源码跟踪,最终交给ILoadBalancer类去选择服务实例。

ILoadBalancer在ribbon-loadbalancer的jar包下,ILoadbalancer是一个借口,该借口定义了一系列实现负载均衡的方法:

在这里插入图片描述
其中,addServers()方法用于添加一个Server集合,chooseServer()方法用于根据key去获取Server,markServerDown()方法用于标记某个服务下线,getReachableServers()获取可用的Server集合,getAllServers()获取所有的Server集合。

ILoadBalancer的子类为BaseLoadBalancer,BaseLoadBalancer的实现类为DynamicServerListLoadBalancer,三者之间的关系如下:
在这里插入图片描述

查看DynamicServerListLoadBalancer类的源码,DynamicServerListLoadBalancer需要配置IClientConfig、IRule、IPing、ServerList、ServerListFilter和ILoadBalancer。查看BaseLoadBalancer类的源码,在默认情况下,实现了如下配置:

IClientConfig ribbonClientConfig: DefaultClientConfigImpl

IRule ribbonRule: RoundRobinRule

IPing ribbonPing: DummyPing

ServerList ribbonServerList: ConfigurationBasedServerList

ServerListFilter ribbonServerListFilter: ZonePreferenceServerListFilter

ILoadBalancer ribbonLoadBalancer: ZoneAwareLoadBalancer

IClientConfig用于配置负载均衡的客户端,IClientConfig的默认实现类为DefaultClientConfigImpl。

IRule用于配置负载均衡的策略,IRule有3个方法,其中choose()是根据key来获取server实例的,setLoadBalancer()和getLoadBalancer()是用来设置和获取ILoadBalancer的,它的源码如下:

在这里插入图片描述

IRule有很多默认的实现类,这些实现类根据不同的算法和逻辑来处理负载均衡的策略。IRule的默认实现类有以下7种。在大多数情况下,这些默认的实现类是可以满足需求的,如果有特殊的需求,可以自己实现。

  1. BestAvailableRule:选择最小请求数。
  2. ClientConfigEnabledRoundRobinRule: 轮询。
  3. RandomRule: 随机选择一个server。
  4. RoundRobinRule: 轮询选择 server。
  5. RetryRule:根据轮询的方式重试。
  6. WeightedResponse TimeRule:根据响应时间去分配一个weight ,weight 越低,被选择的可能性就越低。
  7. ZoneAvoidanceRule: 根据server的zone区域和可用性来轮询选择。

在这里插入图片描述

IPing用于向server发送"ping",来判断server是否有响应,从而判断server是否可用。它有一个isAlive()方法:
在这里插入图片描述

IPing的实现类有PingUrl、PingConstant、NoOpPing、DummyPing和NIWSDiscoveryPing。

在这里插入图片描述

  • PingUrl:真实地去piing某个Url,判断其是否可用
  • PingConstant:固定返回某服务是可用,默认返回true,即可用
  • NoOpPing:不去ping,直接返回true,即可用
  • DummyPing:直接返回true,并实现了initWithNiwsConfig方法
  • NIWSDiscoveryPing:根据DIscoveryEnabledServer的InstanceInfo的InstanceStatus去判断,如果为InstanceStatus.UP,则可用,否则不可用

ServerList是定义获取所有server的注册列表信息的接口:
在这里插入图片描述
ServerListFilter接口定义了可根据配置去过滤或者特性动态地获取符合条件的server列表的方法:
在这里插入图片描述
阅读DynamicServerListLoadBalancer的源码,DynamicServerListLoadBalancer的构造函数中有一个initWithNiwsConfig()方法。在该方法中经过一系列的初始化配置,最终执行了restOfInit()方法。DynamicServerListLoadBalance的部分源码如下:

在这里插入图片描述

在restOfInit()方法中,有一个updateListOfServers()的方法,该方法是用来获取所有的ServerList的。

void restOfInit(IClientConfig clientConfig){
		.....
		updateListOfServers();
		......
}

进一步跟踪updateListOfServers()方法的源码,最终由serverListImpl.getUpdateListOfServers()获取所有的服务列表:

在这里插入图片描述

而serverListImpl是ServerList 接口的具体实现类。跟踪源码,ServerList 的实现类为DiscoveryEnabledNIWSServerList,这个类在ribbon-eureka.jar 的com.netlix.niws.loadbalancer包下。

其中,DiscoveryEnabledNIWSServerList 有getlnitiallistOfServers()和getUpdatedListOfServers()方法,具体代码如下:
在这里插入图片描述

继续跟踪源码,obtainServersViaDiscovery() 方法是根据eurekaClientProvider.get()方法来获取EurekaClient的,再根据EurekaClient来获取服务注册列表信息,代码如下:
在这里插入图片描述
其中,eurekaClientProvider 的实现类是LegacyEurekaClientProvider,LegacyEurekaClientProvider是一个获取eurekaClient实例的类,其代码如下:
在这里插入图片描述

由此可见,负载均衡器是从Eureka Client获取服务列表信息的,并根据IRule的策略去路由,根据IPing去判断服务的可用性。

那么现在还有一个问题,负载均衡器每隔多长时间从EurekaClient获取注册信息呢?

在BaseLoadBalancer类的源码中,在BaseLoadBalancer 的构造方法开启了一个PingTask任务,代码如下:

public BaseLoadBalancer(String name, IRule rule, LoadBalancerStats stats, IPing ping, IPingStrategy pingStrategy){
	........
	setupPingTask();
	........
}

在setupPingTask()的具体代码逻辑里,开启了ShutdownEnabledTimer的PingTask任务,在默认情况下,变量pingIntervalSeconds的值为10, 即每10秒向EurekaClient发送一次心跳‘ping”
在这里插入图片描述
查看PingTask的源码,PingTask创建了一个Pinger对象,并执行了runPinger()方法。

在这里插入图片描述

查看Pinger的runPinger()方法,最终根据pingerStrategy.pingServers(ping, allServers)来获取服务的可用性:

  • 如果该返回结果与之前相同,则不向EurekaClient获取注册列表;
  • 如果不同,则通知ServerStatusChangeListener服务注册列表信息发生了改变,进行更新或者重新拉取;

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

由此可见,LoadBalancerClient是在初始化时向Eureka获取服务注册列表信息,并且每10秒向EurekaClient发送“ping”,来判断服务的可用性。如果服务的可用性发生了改变或者服务数量和之前的不一致,则更新或者重新拉取。LoadBalancerClient 有了这些服务注册列表信息,就可以根据具体的IRule的策略来进行负载均衡。

最后,回到问题的本身,为什么在RestTemplate类的Bean上加一个@LoadBalance 注解就可以使用Ribbon的负载均衡呢?
全局搜索查看有哪些类用到了@LoadBalanced注解。通过搜索,可以发现LoadBalancerAutoConfiguration类(LoadBalancer 自动配置类)使用到了该注解,LoadBalancerAutoConfiguration 类的代码如下:

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

综上所述,Ribbon的负载均衡主要是通过LoadBalancerClient来实现的,而LoadBalancerClient具体交给了ILoadBalancer 来处理,ILoadBalancer 通过配置IRule、IPing 等,向EurekaClient获取注册列表的信息,默认每10秒向EurekaClient发送一次“ping”,进而检查是否需要更新服务的注册列表信息。最后,在得到服务注册列表信息后,ILoadBalancer根据IRule的策略进行负载均衡。

而RestTemplate加上@LoadBalance注解后,在远程调度时能够负载均衡,主要是维护了一个被@LoadBalance注解的RestTemplate 列表,并给该列表中的RestTemplate 对象添加了拦截器。在拦截器的方法中,将远程调度方法交给了Ribbon 的负载均衡器LoadBalancerClient去处理,从而达到了负载均衡的目的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值