1
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中

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

3 loadBalanceClient 原理

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

(1)RibbonAutoConfiguration,

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

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

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

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

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

问题:为什么要用Spring的容器?AnnotationConfigApplicationContext? 因为区分环境后,在后面注入serverlist的时候,直接通过@bean在参数里注入即可,不需要用serviceid判断。

(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

拳码码
15 声望8 粉丝

开拓餐饮新纵横。