Ribbon几种负载均衡策略代码解析
我们都知道SpringCloud中的本地负载均衡组件Ribbon,Feign中集成了Ribbon和Hystrix,使得我们使用Ribbon变得更加方便。
Ribbon中的负载均衡策略都继承自AbstractLoadBalancerRule这个抽象类。
AbstractLoadBalancerRule 实现了 IRule 和 IClientConfigAware 这两个接口。
1) IClientConfigAware这个很好理解,看它的方法就知道,用于加载配置信息的。
2) IRule中的choose()方法是核心方法,具体负载均衡策略核心逻辑就在这个方法中,它返回的是是一个Server,代表请求将要调用的服务。IRule还将ILoadBanlancer这个类复合进我们的负载均衡策略类中,因为到时候需要使用ILoadBanlancer中的获取服务列表等操作。
一、RandomRule
最简单的策略,从代码来看分为以下步骤:
1. Random rand = new Random(); //使用java自带的随机数生成类
2. List<Server> upList = lb.getReachableServers();// 可用服务列表
List<Server> allList = lb.getAllServers();//所有服务列表
3. in serverCount = allList.size()
2. int index = this.rand.nextInt(serverCount); //随机数范围是0到serverCount(不包括serverCount)
3. server = (Server)upList.get(index); //上面范围是以所有服务的列表大小算,但是取值却从upList取,为啥,懂的评论区留言提点下我
部分代码截图:
二、RoundRobinRule
轮询是Ribbon默认的负载均衡策略,逻辑也比较简单,定义一个AtomicInteger初始值为0(不断调用choose时,这个值是不停增长的)每次用它加1后对serverCount取余,获取到的int值用来从allServers列表中get一个server,同时使用CAS使AtomicInteger的值加1。
获取到的server需要判断是否可用,可用则返回,不可用继续AtomicInteger取余那个过程。
以上轮询最多只会重试10次(通过while循环),如果还获取不到可用server则打印warn日志,直接返回null
看下主要步骤的代码:
int nextServerIndex = this.incrementAndGetModulo(serverCount); server = (Server)allServers.get(nextServerIndex);
这个是取余方法:
private int incrementAndGetModulo(int modulo) { int current; int next; do { current = this.nextServerCyclicCounter.get(); // 这就是刚才我说的AtomicInteger变量,这是一个类成员变量 next = (current + 1) % modulo; } while(!this.nextServerCyclicCounter.compareAndSet(current, next)); return next; }
三、RetryRule
首先我们从开头就可以看到,RetryRule是基于RoundRobinRule的:
接下来分析他的choose方法,看看他和普通轮询有啥不同:
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; }
第一步:首先它利用请求调用的当前时间,加上最大允许重试时间,得到了一个终止时间。
第二步:然后调用轮询策略里的choose
第三步:返回的服务不可用并且还没超出终止时间,则进入下一步重试逻辑
第四步:构造一个定时任务InterruptTask,这个InterruptTask会在到终止时间的时候,将当前线程interupt
第五步:只要当前线程未被中断,重新调用轮询的choose,这里得到有用的server的话,就直接跳出循环,然后返回server了
第六步:第五步如果得到的server不可用,那么将当前线程暂停(转换成可执行状态)
总结:
RoundRobinRule如果轮询获取到的server不可用,最多会重试9次,直到返回可用server;
RetryRule如果轮询获取到的server不可用,只会重试一次,还不行就会将当前线程暂停,当然有可能刚暂停的线程立马又被调度;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理