首页 > 编程语言 >Java学习:ribbon的常用负载均衡算法分析

Java学习:ribbon的常用负载均衡算法分析

时间:2023-01-15 10:06:39浏览次数:41  
标签:lb 负载 return Server server choose Java null ribbon


1.Ribbon介绍

因为微服务是目前互联网公司比较流行的架构,所以spring就提供了一个顶级框架-spring cloud,来解决我们在开发微服务架构中遇到的各种各样的问题,今天的主角是spring cloud 框架中集成的组件Ribbon,那么Ribbon能解决什么问题呢,我们来思考下面的问题。

微服务架构中的每个服务为了高可用,很大程度上都会进行集群,我们假设现在集群了3个user服务,同时能提供相同的服务,问题来了,我们如何决定调用这3个user服务中的哪一个呢?

根据不同分析角度,会有不同的答案,也可以理解为根据不同的情况,我们可以写不同的算法,来决定到底此时此刻,调用这3个user服务的哪一个,那么,Ribbon就给我们提供了不同的算法,我们可以根据业务场景,调整配置文件,决定到底使用哪个算法,这样,算法中就会计算出调用哪个user服务了。

2.准备工作

1)我们准备一个eureka注册中心

2)再准备一个order服务

3)再准备3个相同代码的user服务,这样,order服务通过eureka注册中心,就可以发现user的3个服务

3.Ribbon的常用负载均衡策略

Ribbon是通过IRule的这个接口来选择3个user服务中的哪个的,但是实际执行的代码肯定是继承了这个接口的实现类,所以选择不同的实现类,就会选择不同负载均衡策略

public interface IRule {

Server choose(Object var1);

void setLoadBalancer(ILoadBalancer var1);

ILoadBalancer getLoadBalancer();
}

3.1. RoundRobinRule 轮询策略

此策略是Ribbon的默认策略,是按照顺序,依次对所有的user服务进行访问。

通过重写IRule的choose方法,来选择并返回决定调用的user服务,在下面的源码中,List allServers = lb.getAllServers(); 获得了所有的3个user服务实例,int nextServerIndex = this.incrementAndGetModulo(serverCount); 保存了当前调用的user实例的序号,然后就可以按照顺序调用下一个user服务了

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;
}
}
}

debug的图例:

 

Java学习:ribbon的常用负载均衡算法分析_算法

 

3.2. RoundRobinRule 随机策略

就和这个策略的名字一样,是对user的3个服务的随机调用,所以不存在规律,如下源码中int index = this.chooseRandomInt(serverCount); 通过随机数来选择下标,所以对user服务的调用是随机的

public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
} else {
Server server = null;

while(server == null) {
if (Thread.interrupted()) {
return null;
}

List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}

int index = this.chooseRandomInt(serverCount);
server = (Server)upList.get(index);
if (server == null) {
Thread.yield();
} else {
if (server.isAlive()) {
return server;
}

server = null;
Thread.yield();
}
}

return server;
}
}

debug的图例:

 

Java学习:ribbon的常用负载均衡算法分析_java_02

 

3.3. WeightedResponseTimeRule响应时间加权重策略

根据user的3个服务的响应时间来分配权重,响应时间越长的服务,权重越低,那么被调用的概率也就越低。相反,响应时间越短的服务,权重越高,被调用的概率也就越高

响应时间加权重策略的实现分为两步:

  1. WeightedResponseTimeRule实现类中默认情况下每隔30秒会统计一次每个服务的权重,在此30秒内,用的是轮询策略
  2. 30秒之后,会根据统计的结果来分配每个实例的权重,然后根据权重来分配调用次数
extends RoundRobinRulepublic Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
} else {
Server server = null;

while(server == null) {
List<Double> currentWeights = this.accumulatedWeights;
if (Thread.interrupted()) {
return null;
}

List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}

int serverIndex = 0;
double maxTotalWeight = currentWeights.size() == 0 ? 0.0D : (Double)currentWeights.get(currentWeights.size() - 1);
//在30秒之内,maxTotalWeight变量会一直是0.0
if (maxTotalWeight >= 0.001D && serverCount == currentWeights.size()) {
double randomWeight = this.random.nextDouble() * maxTotalWeight;
int n = 0;

for(Iterator var13 = currentWeights.iterator(); var13.hasNext(); ++n) {
Double d = (Double)var13.next();
if (d >= randomWeight) {
serverIndex = n;
break;
}
}

server = (Server)allList.get(serverIndex);
} else {
server = super.choose(this.getLoadBalancer(), key);
if (server == null) {
return server;
}
}

if (server == null) {
Thread.yield();
} else {
if (server.isAlive()) {
return server;
}

server = null;
}
}

return server;
}
}

debug的图例:

 

Java学习:ribbon的常用负载均衡算法分析_java_03

 

3.4. RetryRule 重试策略

重试策略是指通过轮询策略选出一个实例,然后去访问,如果此实例为null或者已经失效,那么会重试其他的实例,answer = this.subRule.choose(key); 会根据轮询策略选择一个实例,然后if ((answer == null || !answer.isAlive()) && System.currentTimeMillis() < deadline)判断如果实例为null或者失效,那么会重新选择

public Server choose(ILoadBalancer lb, Object key) {
long requestTime = System.currentTimeMillis();
long deadline = requestTime + this.maxRetryMillis;
Server answer = null;
answer = this.subRule.choose(key);
if ((answer == null || !answer.isAlive()) && System.currentTimeMillis() < deadline) {
InterruptTask task = new InterruptTask(deadline - System.currentTimeMillis());

while(!Thread.interrupted()) {
answer = this.subRule.choose(key);
if (answer != null && answer.isAlive() || System.currentTimeMillis() >= deadline) {
break;
}

Thread.yield();
}

task.cancel();
}

return answer != null && answer.isAlive() ? answer : null;
}

 

Java学习:ribbon的常用负载均衡算法分析_分布式_04

 

3.5. BestAvailableRule 最低并发策略

会根据每个服务实例的并发数量来决定,访问并发数最少的那个服务,int concurrentConnections = serverStats.getActiveRequestsCount(currentTime); 会获得当前遍历的实例的并发数,然后和其他的实例的并发数进行判断,最终访问并发量最少的那个实例

public Server choose(Object key) {
if (this.loadBalancerStats == null) {
return super.choose(key);
} else {
List<Server> serverList = this.getLoadBalancer().getAllServers();
int minimalConcurrentConnections = 2147483647;
long currentTime = System.currentTimeMillis();
Server chosen = null;
Iterator var7 = serverList.iterator();

while(var7.hasNext()) { //遍历所有的实例
Server server = (Server)var7.next();
ServerStats serverStats = this.loadBalancerStats.getSingleServerStat(server);
if (!serverStats.isCircuitBreakerTripped(currentTime)) {
int concurrentConnections = serverStats.getActiveRequestsCount(currentTime); //判断并发数,并和已经判断出的最少的并发数比较
if (concurrentConnections < minimalConcurrentConnections) {
minimalConcurrentConnections = concurrentConnections;
chosen = server;
}
}
}

if (chosen == null) {
return super.choose(key);
} else {
return chosen;
}
}
}

3.6. AvailabilityFilteringRule 可用过滤策略

此策略会聪明的过滤掉一直失败并被标记为circuit tripped的user服务,而且会过滤掉那些高并发的user服务

public Server choose(Object key) {
int count = 0;

for(Server server = this.roundRobinRule.choose(key); count++ <= 10; server = this.roundRobinRule.choose(key)) {
//通过predicate来过滤
if (this.predicate.apply(new PredicateKey(server))) {
return server;
}
}
//过滤掉一些服务之后,会采用轮询的方式调用剩下的服务
return super.choose(key);
}

3.7. ClientConfigEnabledRoundRobinRule 自定义策略

此策略本身并没有实现什么特殊的处理逻辑,但是可以通过重置LoadBalancer来达到自定义一些高级策略的目的,可以重写initWithNiwsConfig和setLoadBalancer

public void initWithNiwsConfig(IClientConfig clientConfig) {
this.roundRobinRule = new RoundRobinRule();
}

public void setLoadBalancer(ILoadBalancer lb) {
super.setLoadBalancer(lb);
this.roundRobinRule.setLoadBalancer(lb);
}

public Server choose(Object key) {
if (this.roundRobinRule != null) {
return this.roundRobinRule.choose(key);
} else {
throw new IllegalArgumentException("This class has not been initialized with the RoundRobinRule class");
}
}

标签:lb,负载,return,Server,server,choose,Java,null,ribbon
From: https://blog.51cto.com/u_8238263/6008216

相关文章

  • Java教程学习:揭秘什么是面向接口编程
    先用一个案例来给大家说明一下面向接口编程。案例:有一个电脑类(Computer),电脑除了有基本的开机关机功能外,还有连接任何外接设备的功能,比如能电脑能连接外置键盘(Keyboard),鼠标......
  • java基础教程:IO流
    一:IO流的分类1)按流向分:输入流:读取数据,把持久设备的数据读取到内存中。输出流:写出数据,把内存的数据写出到持久设备。2)按数据类型分:计算机中一切数据都是:字节数据。字符数据:底......
  • Java基础教程:ArrayList入门
    1ArrayList类概述什么是集合提供一种存储空间可变的存储模型,存储的数据容量可以发生改变ArrayList集合的特点底层是数组实现的,长度可以变化泛型的使用用于约束集合中存储......
  • java:Redis持久化
    一.redis持久化的介绍Redis的持久化指的是将内存中redis数据库运行的数据,写到硬盘文件上。Redis持久化的意义主要在于故障恢复,比如你部署一个Redis,作为缓存有可能里边有......
  • JavaScript 中搜索数组元素的四种方法
    在实际开发当中,我们经常会遇到类似诸如下面的需求:获取满足特定条件的数组中的所有项目要检查是否满足条件?检查数组中是否有特定值?在数组中找到指定值的索引?在本文中,我们将讨......
  • 《跟老卫学 HarmonyOS 开发》:DevEco Studio 启用Java预览器
    老版的DevEcoStudio只支持layout资源类型的XML文件的预览。在新版的DevEcoStudio已经能够支持 Ability/AbilitySlice的Java类文件的预览。新版的DevEcoStudio默认......
  • 【Java 数据结构及算法实战】系列 013:Java队列07——双端队列Deque
    双端队列(Deque),顾名思义是可以在队列的两端插入和移除元素的特殊队列。Java提供了java.util.Deque<E>接口以提供对双端队列的支持。该接口是JavaCollectionsFramework的一......
  • 【Java数据结构及算法实战】系列008:Java队列02——阻塞队列BlockingQueue
    阻塞队列(BlockingQueue)是一种支持额外操作的队列,这两个附加的操作是:l  在队列为空时,获取元素的线程会等待队列变为非空。l  当队列满时,存储元素的线程会等待队列可用。J......
  • Docker部署Java项目运行命令脚本
    项目名称x-schools-server部署教程cd/app/webapps/x-schools-server#全部移除cd/app/webapps/x-schools-serverdockerstop$(dockerps-aqf"name=x-schools-......
  • Java 设置windows系统Maven 环境变了
    ::添加环境变量BAT_HOME@echooffecho添加bat环境变量setregpath=HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SessionManager\Environmentsetevname......