4-Spring cloud之搭建Ribbon负载均衡——服务器上实操(下)
- 1. 前言
- 1.1 Ribbon负载均衡架构图
- 2. Ribbon自带的负载均衡
- 2.1 Ribbon自带的负载均衡算法
- 2.2 自带负载均衡之间的切换
- 3. Ribbon自定义负载均衡
- 3.1 自定义Ribbon负载均衡(简单试用)
- 3.1.1 添加自定义轮询策略配置类
- 3.1.2 启动类添加注解@RibbonClient
- 3.1.3 重启启动服务消费者,看效果
- 3.2 自定义Ribbon负载均衡(自定义算法)
- 3.2.1 首先查看Ribbon轮询策略的源码
- 3.2.2 创建自己的轮询策略
- 3.2.2.1 新建自定义轮询策略类
- 3.2.2.2 启用自定义轮询策略类
1. 前言
1.1 Ribbon负载均衡架构图
- 我们在上篇文章的基础上继续Ribbon的负载均衡,为了更清晰,再放一次架构图,如下:
关于图的更多解释,请看Ribbon负载均衡上篇。 - 关于上篇请看下面文章,如下:
3-Spring cloud之搭建Ribbon负载均衡——服务器上实操(上).
2. Ribbon自带的负载均衡
2.1 Ribbon自带的负载均衡算法
-
Ribbon负载均衡的规则都定义在IRule接口中,而IRule有很多不同的实现类,如下:
-
简单说一下这些规则
- RoundRobinRule:轮询策略。
- WeightedResponseTimeRule
- 根据平均响应时间计算服务的权重,响应时间越快服务权重越大杯选中的概率越高。刚启动时如果统计信息不足,则使用 RoundRobinRule(轮询策略),等统计信息足够,则会切换到 WeightedResponseTimeRule 策略。
- RandomRule
- 随机策略,即:随机选择一个可用的服务器。
- RetryRule
- 重试机制的选择策略
- 先按 RoundRobinRule(轮询策略)的策略获取服务,如果获取服务失败,则在指定时间内进行重试,获取可用的服务。
- BestAvailableRule
- 先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。
- AvailabilityFilteringRule
- 先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务(并发连接的上限,可以由客户端的
ActiveConnectionsLimit
属性进行配置),然后对剩余的服务列表按照RoundRobinRule(轮询策略)的策略进行访问。
- 先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务(并发连接的上限,可以由客户端的
- ZoneAvoidanceRule
- 以区域可用的服务器为基础进行服务器的选择,使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后对zone内的多个服务做轮询策略。
- ZoneAvoidanceRule是默认实现。基于分区下的服务器的可用性选出可用分区列表,再从可用分区列表中随机选择一个分区,采用轮询的策略选择该分区的一个服务器。
- 详细的讲解可以参考下面的文章,很不错,如下:
Ribbon架构篇 - ZoneAvoidanceRule.
- 详细的讲解可以参考下面的文章,很不错,如下:
2.2 自带负载均衡之间的切换
- 上篇文章已经知道,Ribbon默认的策略是轮询策略,那么怎么切换成别的策略呢?只需在配置类里加几行代码即可,想用什么策略自己修改即可,如下:
@Bean //如果此处没有配置就是默认的轮询策略,如果有配置就取配置的策略 public IRule getMyIRule(){ // return new RoundRobinRule();//轮询策略 // return new RandomRule();//随机策略 // return new RetryRule();//重试机制的选择策略 return new WeightedResponseTimeRule();//权重策略 // …… }
3. Ribbon自定义负载均衡
3.1 自定义Ribbon负载均衡(简单试用)
3.1.1 添加自定义轮询策略配置类
- 注意:此配置类不能与主启动类同目录;
- 这里只是为了简单演示此配置类生效问题,所以用的算法还是上面提到过的Ribbon负载的算法,如下:
package com.liu.susu.config.ribbon; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import com.netflix.loadbalancer.RoundRobinRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Description 自定义轮询策略 * @Author susu */ @Configuration public class MyRibbonRule { //注意:此配置类不能与主启动类同目录 @Bean public IRule getMyIRule(){ return new RandomRule();//随机策略 // return new RoundRobinRule();//轮询 } }
3.1.2 启动类添加注解@RibbonClient
- 如下:
//此注解用来加载自定义的ribbon负载均衡配置类MyRibbonRule.java @RibbonClient(name = "DOG-PROVIDER", configuration = MyRibbonRule.class)
3.1.3 重启启动服务消费者,看效果
- 这里就不再演示了,就是访问多点几次看看效果有没能达到。
3.2 自定义Ribbon负载均衡(自定义算法)
3.2.1 首先查看Ribbon轮询策略的源码
- 如下:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package com.netflix.loadbalancer; import com.netflix.client.config.IClientConfig; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RoundRobinRule extends AbstractLoadBalancerRule { private AtomicInteger nextServerCyclicCounter; private static final boolean AVAILABLE_ONLY_SERVERS = true; private static final boolean ALL_SERVERS = false; private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class); public RoundRobinRule() { this.nextServerCyclicCounter = new AtomicInteger(0); } public RoundRobinRule(ILoadBalancer lb) { this(); this.setLoadBalancer(lb); } public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { log.warn("no load balancer"); return null; } else { Server server = null; int count = 0; while(true) { if (server == null && count++ < 10) { List<Server> reachableServers = lb.getReachableServers(); List<Server> allServers = lb.getAllServers(); int upCount = reachableServers.size(); int serverCount = allServers.size(); if (upCount != 0 && serverCount != 0) { int nextServerIndex = this.incrementAndGetModulo(serverCount); server = (Server)allServers.get(nextServerIndex); if (server == null) { Thread.yield(); } else { if (server.isAlive() && server.isReadyToServe()) { return server; } server = null; } continue; } log.warn("No up servers available from load balancer: " + lb); return null; } if (count >= 10) { log.warn("No available alive servers after 10 tries from load balancer: " + lb); } return server; } } } private int incrementAndGetModulo(int modulo) { int current; int next; do { current = this.nextServerCyclicCounter.get(); next = (current + 1) % modulo; } while(!this.nextServerCyclicCounter.compareAndSet(current, next)); return next; } public Server choose(Object key) { return this.choose(this.getLoadBalancer(), key); } public void initWithNiwsConfig(IClientConfig clientConfig) { } }
3.2.2 创建自己的轮询策略
3.2.2.1 新建自定义轮询策略类
- 设计思路:
将原来的轮询改成轮询的时候每台服务器执行两次。 - 根据上面自带的轮询策略进行修改,主要修改代码,3处,如下:
- 修改后的完整代码如下:
package com.liu.susu.config.ribbon; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * @Description 自定义新的轮询策略(根据源码的轮询策略修改) * @Author susu */ public class MyRoundRobinRule extends AbstractLoadBalancerRule { private AtomicInteger nextServerCyclicCounter; private static final boolean AVAILABLE_ONLY_SERVERS = true; private static final boolean ALL_SERVERS = false; private static Logger log = LoggerFactory.getLogger(MyRoundRobinRule.class); private int exeTimes =0; //每台服务器被访问执行调用的次数 public MyRoundRobinRule() { this.nextServerCyclicCounter = new AtomicInteger(0); } public MyRoundRobinRule(ILoadBalancer lb) { this(); this.setLoadBalancer(lb); } public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { log.warn("no load balancer"); return null; } else { Server server = null; int count = 0; exeTimes ++; while(true) { if (server == null && count++ < 10) { List<Server> reachableServers = lb.getReachableServers(); List<Server> allServers = lb.getAllServers(); int upCount = reachableServers.size(); int serverCount = allServers.size(); if (upCount != 0 && serverCount != 0) { int nextServerIndex = this.nextServerCyclicCounter.get(); if (exeTimes >= 2){ nextServerIndex = this.incrementAndGetModulo(serverCount); exeTimes = 0; } server = (Server)allServers.get(nextServerIndex); if (server == null) { Thread.yield(); } else { if (server.isAlive() && server.isReadyToServe()) { return server; } server = null; } continue; } log.warn("No up servers available from load balancer: " + lb); return null; } if (count >= 10) { log.warn("No available alive servers after 10 tries from load balancer: " + lb); } return server; } } } private int incrementAndGetModulo(int modulo) { int current; int next; do { current = this.nextServerCyclicCounter.get(); next = (current + 1) % modulo; } while(!this.nextServerCyclicCounter.compareAndSet(current, next)); return next; } public Server choose(Object key) { return this.choose(this.getLoadBalancer(), key); } public void initWithNiwsConfig(IClientConfig clientConfig) { } }
3.2.2.2 启用自定义轮询策略类
- 如下:
- 启动,测试看效果
好了,到这里吧,具体的新轮询效果还是自己点点测试吧,我这里是可行的。