SpringCloud 之Ribbon的负载均衡策略

Ribbon负载均衡策略的研究

试验样例:

1、ribbon-demo

2、Provider-demo1 、provider-demo2

 

1 负载规则

Ribbon负载规则是通过实现IRule接口,并将接口实现注入到对应的serviceId的负载均衡器中,其中涉及到了:规则的选择(choose)、负载实例的健康检查(IPing)、定时更新、IPingStrategy

核心代码组件:

(1) BaseLoadBalancer

(2) IPing

(3) IPingStrategy

(4) PingTask

(5) IRule

1.1 IRule负载规则

Ribbon 内部通过实现IRule接口实现多实例服务请求的负载的选择。该接口有三个方法:

 

1.Choose方法,是通过提供的key(即ServiceId),根据负载规则从ServerList中选择一个可用服务,这里面涉及到一个非常关键的点“可用服务”(reachService),在后面的IPing中会讲到Ribbon是如何做服务的可用性检查的。

2.SetLoadBalancer该方法是将一个LoadBalancer注入近来,一般是RibbonLoadBalancerClient类,该类主要用于规则的内部使用。

3.GetLoadBalancer

对于IRule接口Ribbon内部已经对其进行了一部分实现,除非用户业务过于复杂,复杂到需要自定义处理规则的时候,否则内部提供的负载规则已经足够用了,下面是几个主要的负载规则:

RandomRule(随机)、RoundRobinRule(rr轮询)、WeightedResponseTimeRule(响应时间权重)、RetryRule(重试)、BestAwailableRule(最佳可用规则:直译的)等等。一般我们用到的就属:Random、retry、rr了,相应时间权重一般是在多实例配置不同时才会用到这个。

1.2 BaseLoadBalancer

BaseLoadBalancer是Ribbon的核心类,主要提供Ribbon负载均衡处理的核心业务逻辑,包括负载server的选择、server可用性检查,其子类有:DynamicServerListLoadBalancer和ZoneAwareLoadBalancer,RibbonLoadBalancerClient通过其中子类获取到负载后的Server信息,并执行Execute方法实现最终的调用。在该类中非常重要的几个属性:

IPing:做server可用性检查接口

IRule:server选择规则接口

allServerList :所有server集合是一个ArrayList

upServerList: 可用server集合,可以想到是通过IPing执行后最终得到的可用集合

lbTimer :用来执行IPing的schedule定时器

 

 

这两个属性是用来控制在进行检查时需要的参数控制,第一个是定时器的执行频率、第二个是定时器执行一次超时时间。

 

 

两个可重入的读写锁 ,这两个锁的用途将在服务列表更新过程中控制并发使用。

IPingStrategy:主要用于ping的执行策略,在BaseLoadBalancer内部实现了一个默认的执行策略:SerialPingStrategy。

 

 

1.2.1 BaseLoadBalancer初始化过程

BaseLoadBalancer构造方法有六个,这一只将其中一个,也是设置最全的一个:

 

 

在这里可以看到,在构造方法中设置了name、ping、strategy、rule,并同时启动了setupPingTask,该方法的主要目的是启动schedule定时任务:

 

 

接着是设置整个Balancer的状态,最后一步init()初始化,这里面主要是添加监听:   

 

 

整个BaseLoadBalancer初始化过程就完成了。

1.2.2 负载过程

负载过程其实质是在一个服务的实例列表中按照某种方式从中选择出一个作为本次调用的实例的过程。关键在于选择二字。在选择过程中关键的几个变量:IRule、AllServerList、upServerList。

BaseLoadBalancer对外提供了chooseServer()、choose()两个方法,其实质是一样的只不过返回的数据格式不同,前者返回选中的Server对象,后者则返回的是选中的Server对象的id。具体内容如下:

 

 

 

可以发现其内部实现基本一致,最终都是通过Rule规则进行Server的选择。对于规则内部在前文中已经进行了描述,这里就不在赘述了。

1.2.3 IPing实现Server检查

为了保证服务的可用性,Ribbon在进行负载均衡前,需要对allServerList进行筛选,从中获取可用服务,并将可用服务替换到upServerList中,在整个处理过程中与上一节负载过程不冲突,检查过程是通过Timer执行的。将PingTask作为处理任务,定时执行。

具体步骤如下:

1.Timer启动

2.PingTask是一个TimerTask,被定时执行,内部调用Ping实例,runPinger方法:

 

 

3.方法内部调用策略执行IPing接口

   IPing的是一个接口,该接口Ribbon内部也设计了几个类:PingUrl、DummyPing、NoOpPing、PingConstant。我在研发过程中一般采用的是PingUrl,这样可以做真实的实例可用性检查,如果采用PingUrl,需要对应实例提供无权限验证的接口,作为检查接口。还需要注意的是如果服务端提供的接口有具体的URI,则需要通过PingURL的构造参数将具体的URI设置,否则就会报unavailable instances 错误,这个问题我之前遇到过,出问题时感觉这是一个坑,后来通过debug才发现Ribbon的设计人员已经考虑到这一点了,所以提供了一个带参数的构造方法给大家自定义URI。

 

 

2  @LoadBalanced接入方式

  @LoadBalanced这个元注解只是一个标记,用来配合spring boot 的自动装配机制,当在项目中添加了该注解,则Spring在启动时将加载 LoadBalancerAutoConfiguration类进行ribbon负载均衡的配置。在该类的源码中除了有LoadBalance服务所需要的一些基础类的初始化之外,还有一个非常重要的Bean被创建:LoadBalancerInterceptor是一个实现了ClientHttpRequestInterceptor接口的拦截器。这个实现类就是Ribbon接入RestTempalte 的入口,当客户端应用调用RestTemplate进行远程调用时,会被其拦截,并执行URL的负载。

 

posted @ 2018-03-28 19:54  itcrazer  阅读(103)  评论(0)    收藏  举报