ribbon原理+源码解析

1 总体流程图

image.png

核心:
(1)@balance,最终让template 加一层filter,作负载均衡。
(2)负载均衡用的serverlist,来自spring容器环境,一个server一个容器。
(3)定时任务更新serverlist,ping任务更新server的状态。
(4)chooseServer的rule,ping组件都支持扩展。算法

2 ribbon 自动装配-LoadBalanceAutoConfiguration

image.png
(1)@balance 实际上是个类注解,至关于标签,核心是加了@qualify,注入的时候 @balance+@autowired 配合list,能够获得全部加@balance注解的bean,就是获得List<Template>。
(2)生成LoadBalanceInterceptor组件bean,做为拦截器,核心是loadBalanceClient;生成RestTemplateCutomizer组件bean,用来装配interceptor
(3)核心SmartInitialzingSingleton组件bean,调用RestTemplateCutomizer,把interceptor放到每一个template中spring

interceptor原理:使用loadBalanceClient,利用irule规则,从servers中挑选实现负载均衡,用requestFactory建立request,向server发请求。springboot

3 loadBalanceClient 原理

在spring-cloud-netflix-ribbon里,找到了LoadBalanceClient类,经过RibbonAutoConfiguration 自动配置。并发

(1)RibbonAutoConfiguration,负载均衡

@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,  在这个以前,说明是template收集和加入拦截以前要自动配置,说明拦截和收集要用到它,须要先初始化loadbalanceclient。dom

@AutoConfigureAfter(
        name = "[org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration](http://org.springframework.cloud.netflix.eureka.eurekaclientautoconfiguration/)”)

在eureka-client注册以后配置,说明要用eureka的注册表。
在这个配置类里,完成了LoadBalancerClient的@bean初始化。ide

(2)excute过程,先拿ILoadBalance -> getLoadBalancer(serviceId);atom

本质上是从SpringClientFactory中,根据serviceId, 获得对应的AnnotationConfigApplicationContext,而后从这个环境中拿到这个service对应的ILoadBalancer。spa

问题:为何要用Spring的容器?AnnotationConfigApplicationContext? 由于区分环境后,在后面注入serverlist的时候,直接经过@bean在参数里注入便可,不须要用serviceid判断。.net

(3)ILoadBalance的本质,

RibbonClientConfiguration中完成初始化,核心是
new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,serverListFilter, serverListUpdater);
融合了irule组件,ping组件,serverlist,serverupdate,还执行了个restOfInit方法感知eureka。

(4)loadBlance感知eureka

 a 接上面的restOfInit,参数里有个serverlist,是经过bean传递进来,颇有迷惑性,本类的那个bean不是。实际是在EurekaRibbonClientConfiguration的ribbonServerList,注入到了serverListImpl,

 b 本质是restOfInit,  updateListOfServers( ) -> servers = serverListImpl.getUpdatedListOfServers();调用了obtainServersViaDiscovery,从eurekaClientProvider 获得了eureka-client,从而获得了serverlist,存到iloadbalance本地。

(5)获取eureka后的更新serverlist。
仍是restOfInit(),里面启动线程定时更新(30s),enableAndInitLearnNewServersFeature 调用 serverListUpdater.start(updateAction);开启线程。

(6)ping各个server
ping组件也是能够扩展本身写的,不扩展来自于EurekaRibbonClientConfiguration的NIWSDiscoveryPing,isAlive()方法,就是传入server,检查状态是不是InstanceStatus.UP状态。

在zoneLoadBalance的父类 baseLoadBalance的实例化里,initWithConfig ->setPingInterval ,启动了ping线程,runPinger中调用server的isAlive()方法,默认30s一次。返回false的时候,就放到changelist里,返回true的收集起来做新的serverlist。

(7)excute继续,getServer(),实际上是根据irule获得具体某个server,irule好比用atomicLong,递增取模。

总体过程 ,获得IloadBalance->根据irule 算出server->执行excute

4 负载均衡算法种类

RandomRule:随机选取负载均衡策略。
RoundRobinRule:轮询负载均衡策略。
WeightedResponseTimeRule:根据平均响应时间计算全部服务的权重,时间越短权重越大。刚启动时,若是统计信息不足,则使用线性轮询策略,等信息足够时,再切换到WeightedResponseTimeRule。
RetryRule:使用线性轮询策略获取服务,若是获取失败则在指定时间内重试,从新获取可用服务。
BestAvailableRule:继承自ClientConfigEnabledRoundRobinRule。从全部没有断开的服务中,选取到目前为止请求数量最小的服务。
ClientConfigEnabledRoundRobinRule:默认经过线性轮询策略选取服务。经过继承该类,而且对choose方法进行重写,能够实现更多的策略,继承后保底使用RoundRobinRule策略。
AvailabilityFilteringRule:按可用性进行过滤,会先过滤掉因为屡次访问故障而处于断路器跳闸状态的服务,还有并发的链接数超过阈值的服务,而后对剩余的服务列表进行线性轮询。
ZoneAvoidanceRule:自己没有重写choose方法,用的仍是抽象父类PredicateBasedRule的choose。~~~~

5 负载均衡算法使用

(1)正常全局策略

@Configuration
public class LoadBalanced {
    @Bean
    public IRule iRule() {
        return new RandomRule();
    }
}

irule能够换本身的

(2)指定均衡策略

能够注解配置 @RibbonClient(name = "service1", configuration = cn.wbnull.springbootconsumer.config.LoadBalanced.class)

文件配置

service1:
  ribbon:
    NFLoadBalancerRuleClassName: cn.wbnull.springbootconsumer.config.loadbalancer.GlobalRule