我们在spring-cloud中要使用ribbon做负载均衡的时候一般会添加如下包:
并在@Configuration的javaconfig配置中配置如下restTemplate,加上注解@LoadBalanced则实现了前端负载均衡。
下面看看Ribbon的原理:
核心为org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient类:
LoadBalancerClient的实现类为RibbonLoadBalancerClient,这个类是非常重要的一个类,最终的负载均衡的请求处理,由它来执行。
RibbonLoadBalancerClient的核心方法如下:
可知,LoadBalancerClient的选择服务器实例的方法最终是由ILoadBalancer的chooseServer来实现。
ILoadBalancer的实现类为com.netflix.loadbalancer.DynamicServerListLoadBalancer:
其中其核心的属性为IRule和Iping
IRule有很多默认的实现类,这些实现类根据不同的算法和逻辑来处理负载均衡。Ribbon实现的IRule有一下。在大多数情况下,这些默认的实现类是可以满足需求的,如果有特性的需求,可以自己实现。
BestAvailableRule 选择最小请求数
ClientConfigEnabledRoundRobinRule 轮询
RandomRule 随机选择一个server
RoundRobinRule 轮询选择server
RetryRule 根据轮询的方式重试
WeightedResponseTimeRule 根据响应时间去分配一个weight ,weight越低,被选择的可能性就越低
ZoneAvoidanceRule 根据server的zone区域和可用性来轮询选择。
IPing是用来想server发生”ping”,来判断该server是否有响应,从而判断该server是否可用。
PingUrl 真实的去ping 某个url,判断其是否alive
PingConstant 固定返回某服务是否可用,默认返回true,即可用
NoOpPing 不去ping,直接返回true,即可用。
DummyPing 直接返回true,并实现了initWithNiwsConfig方法。
NIWSDiscoveryPing,根据DiscoveryEnabledServer的InstanceInfo的InstanceStatus去判断,如果为InstanceStatus.UP,则为可用,否则不可用。
在DynamicServerListLoadBalancer的构造函数中会调用initWithNiwsConfig,配置相应的IRule和Iping的属性,并调用了restOfInit()方法:
serverListImpl的实现类为com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList类,查看getUpdatedListOfServers()方法:
其中eurekaClientProvider的实现类是LegacyEurekaClientProvider,它是一个获取eurekaClient类,通过加锁的实例方法去获取eurekaClient,其代码如下:
urekaClient的实现类为DiscoveryClient,由此可见,负载均衡器是从EurekaClient获取服务信息,并根据IRule去路由,并且根据IPing去判断服务的可用性。
负载均衡器多久会同步Eureka的注册列表数据呢?
回到最初的DynamicServerListLoadBalancer构造器中,实例化的时候调用了BaseLoadBalancer的构造方法开启了一个PingTask任务,代码如下:
setupPingTask()的具体代码逻辑,它开启了ShutdownEnabledTimer执行PingTask任务,在默认情况下pingIntervalSeconds为10,即每10秒钟,想EurekaClient发送一次”ping”。
查看Pinger的runPinger()方法,最终根据 pingerStrategy.pingServers(ping, allServers)来获取服务的可用性,如果该返回结果,如之前相同,则不去向EurekaClient获取注册列表,如果不同则通知ServerStatusChangeListener或者changeListeners发生了改变,进行更新或者重新拉取。
由此可见,LoadBalancerClient是在初始化的时候,会向EurekaClient拉取服务注册列表,并且向通过10s一次向EurekaClient发送“ping”,来判断服务的可用性,如果服务的可用性发生了改变或者服务数量和之前的不一致,则更新或者重新拉取。LoadBalancerClient有了这些服务注册列表,就可以根据具体的IRule来进行负载均衡。
RestTempalate与Ribbon结合
在该类中,首先维护了一个被@LoadBalanced修饰的RestTemplate对象的List,在初始化的过程中,通过调用customizer.customize(restTemplate)方法来给RestTemplate增加拦截器LoadBalancerInterceptor。
而LoadBalancerInterceptor,用于实时拦截,在LoadBalancerInterceptor这里实现来负载均衡。LoadBalancerInterceptor的拦截方法如下:
查看RibbonLoadBalancerClient的execute方法:
最终由IRule的策略选取实例。
综上所述,Ribbon的负载均衡,主要通过LoadBalancerClient来实现的,而LoadBalancerClient具体交给了ILoadBalancer来处理,ILoadBalancer通过配置IRule、IPing等信息,并向EurekaClient获取注册列表的信息,并默认10秒一次向EurekaClient发送“ping”,进而检查是否更新服务列表,最后,得到注册列表后,ILoadBalancer根据IRule的策略进行负载均衡。
而RestTemplate 被@LoadBalanced注解后,能过用负载均衡,主要是维护了一个被@LoadBalance注解的RestTemplate列表,并给列表中的RestTemplate添加拦截器,进而交给负载均衡器去处理。
参考:https://blog.csdn.net/forezp/article/details/74820899
网友评论