走进Ribbon负载均衡
LB,即负载均衡(Load Balance),在微服务或分布式集群中经常用的一种应用。
负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA。
常见的负载均衡有软件Nginx,LVS;硬件F5等。
相应的在中间件,例如: dubbo和SpringCloud中均给我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义。
负载均衡也分为:
集中式的LB:即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方。
Spring Cloud Ribbon就是基于Netflix Ribbon实现的一套客户端负载均衡器;
是基于RestTemplate的,它赋予了RestTemplate 负载均衡的能力。我们也很容易的自定义负载均衡的算法。
Nginx为服务端(Tomcat/应用) 做负载均衡——服务端负载均衡。
下面就来看看怎么使用Ribbon?
这里接着Eureka使用的案例来继续:
先对用户服务做一个集群,然后订单服务集成Ribbon来实现负载均衡的调用。
Ribbon的工作流程:
首先做用户服务的集群,其实就是“复制”:
1 复制user-server-3000模块,起名为user-server-3001
2 修改pom文件:<artifactId>user-server-3001</artifactId>
3 修改父模块的pom文件,添加<module>user-server-3001</module>
4 修改user-server-3001模块的配置文件中的端口为:3001和服务实例名为user-server-3001
或者
新建一个子模块,内容全部复制过来即可。记得修改端口号和服务实例id
然后在order-server服务集成Ribbon
1 导包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
2 修改RestTemplate的Bean的定义
RestTemplate是 SpringMvc提供的一个基于Rest风格的http调用工具。
加一个@LoadBalanced注解:ribbon的负载均衡标签,赋予RestTemplate有负债均衡的能力。
public RestTemplate restTemplate(){
return new RestTemplate();
}
3 修改Controller调用方式
其实就是修改url。用服务名代替了ip和端口,ribbon就会根据服务名去获取多个目标服务的ip和端口,再用某种算法来实现负载均衡。默认轮询策略。
public User getUserById(Long id){
String url = "http://user-server/userServer/user/"+id;
return restTemplate.getForObject(url, User.class);
}
以上就是ribbon的初步使用,那我们想要改变负载均衡的方式应该怎么做呢?下面先来简单看看ribbon的源码吧。
我们已经知道@LoadBalanced注解赋予RestTemplate负载均衡的能力,那它是怎么赋予的?
在RestTemplate的底层通过一个RibbonLoadBalancerClient.exec客户端工具类来实现负载均衡的,而RibbonLoadBalancerClient.exec方法中做了如下事情:
@LoadBalanced-->LoadBalancerClient接口-->RibbonLoadBalancerClient实现类的execute方法-->
execute方法体里面的getLoadBalancer(服务名)方法获取ILoadBalancer负载均衡器-->
execute方法体里面的getServer方法返回选定的服务-->
通过BaseLoadBalancer.chooseServer方法选择服务-->
chooseServer方法体中会执行this.rule.choose(key)-->
此时就会选择一个IRule接口的实现类去执行这个方法(默认RoundRobinRule)-->
最终调用incrementAndGetModulo方法(最终算法体)
分析了源码之后,我们就知道了Ribbon是通过服务名获取到服务列表后,然后根据一定规则来选择一个服务实例来完成调用。这个规则就叫负载均衡策略。
那这个规则其实就是通过IRule接口定义的,Ribbon自身也实现了不少规则。如下表:
内置负载均衡规则类 |
规则描述 |
RoundRobinRule |
轮询。它是Ribbon默认的负载均衡规则。 |
AvailabilityFilteringRule |
对以下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。 可以通过修改配置loadbalancer.<clientName>.connectionFailureCountThreshold来修改连接失败多少次之后被设置为短路状态。默认是3次。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上线,可以由客户端的<clientName>.<clientConfigNameSpace>.ActiveConnectionsLimit属性进行配置。 |
WeightedResponseTimeRule |
为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小,被选中的概率就越低。 |
ZoneAvoidanceRule |
以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。 |
BestAvailableRule |
会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。 |
RandomRule |
随机选择一个可用的服务器。 |
RetryRule |
先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务。 |
在程序中,我们可以通过配置不同IRule的实现类,选择不同负载均衡策略。当然也可以通过实现IRule接口来完成自定义规则。所以负载均衡策略可以分为内置和自定义。
当我们要改变默认算法时,直接把该内置的实现类加入容器中即可,比如这里的:随机算法的配置
public class BeanConfig {
public RestTemplate restTemplate(){
return new RestTemplate();
}
//配置一个随机的负载均衡的算法
public IRule randomRule(){
return new RandomRule();
}
}