轮询算法分为简单轮询(Round-Robin)和加权轮询(Weighted-Round-Robin)。

简单轮询(Round-Robin)

简单轮询是最简单的一种负载均衡算法,其把来自用户的请求轮流分配给内部的服务器:从服务器1开始,直到服务器N,然后重新开始循环。

public class SimpleRoundRobinLoadBalance implements LoadBalance{private AtomicInteger atomicInteger = new AtomicInteger(0);@Overridepublic ServerInfo select(List<ServerInfo> serverInfos) {if(serverInfos==null || serverInfos.size()==0){return null;}// 获取当前的调用编号(每来一次请求则累加1)int sequence = atomicInteger.getAndIncrement();// 调用编号与服务器个数取余int index = sequence % serverInfos.size();return serverInfos.get(index);}
}

可以看到,简单轮询只需要维护一个递增的请求编号即可,每来1次请求则递增1,为保证线程安全,将其设置为AtomicInteger类型。

简单轮询算法假设所有服务器的性能均相同,不关心每台服务器的当前连接数和响应速度。当请求服务间隔时间变化比较大时,简单轮询算法容易导致服务器间的负载不平衡,故简单轮询适用于服务器组中的所有服务器都有相同的软硬件配置并且平均服务请求相对均衡的情况。

加权轮询(Weighted-Round-Robin)

现实情况下,我们并不能保证每台服务器性能均相近。如果我们将等量的请求分配给性能较差的服务器,这显然是不合理的。因此,这个时候我们需要对轮询过程进行加权,以调控每台服务器的负载。经过加权后,每台服务器能够得到的请求数比例,接近或等于他们的权重比。比如服务器 A、B、C 权重比为 5:2:1。那么在8次请求中,服务器 A 将收到其中的5次请求,服务器 B 会收到其中的2次请求,服务器 C 则收到其中的1次请求。

加权轮询的实现方式就比较多样化:

迭代轮询

其基本思想是用请求序号和所有服务器的总权重求余数mod,然后在服务器间不断循环遍历,每遍历一个服务器,如果该服务器的当前权重>0,则将该服务器的权重减1,同时mod减1,直到mod降为0之后,若当前遍历所在的服务器权重>0,则返回该服务器。

下述代码为迭代轮询的简单实现:

public class WeightedRoundRobinLoadBalance1 implements LoadBalance{public static final String NAME = "roundrobin";private AtomicInteger atomicInteger = new AtomicInteger(0);@Overridepublic ServerInfo select(List<ServerInfo> serverInfos) {// 获取当前的调用编号(每来一次请求则累加1)int sequence = atomicInteger.getAndIncrement();int totalWeight = 0;int maxWeight = 0;int minWeight = 0;Map<ServerInfo, IntegerWrapper> weightMap = new HashMap<>();for(ServerInfo serverInfo: serverInfos){totalWeight += serverInfo.getWeight();maxWeight = Math.max(maxWeight, serverInfo.getWeight());minWeight = Math.min(minWeight, serverInfo.getWeight());weightMap.put(serverInfo, new IntegerWrapper(serverInfo.getWeight()));}int index = sequence % (serverInfos.size());if(minWeight < maxWeight && minWeight >= 0){int mod = sequence % totalWeight;for(int i=0; i<maxWeight; i++){for(Map.Entry<ServerInfo, IntegerWrapper> entry: weightMap.entrySet()){IntegerWrapper value = entry.getValue();// 如果 mod = 0,且权重大于0,返回相应的服务器if(mod == 0 && value.value > 0){return entry.getKey();}// mod != 0,且权重大于0,此时对权重和 mod 分别进行自减操作if(value.value > 0){mod--;value.decrement();}}}}return serverInfos.get(index);}// IntegerWrapper 是一个 int 包装类,主要包含了一个自减方法。// 包装类主要方便更新map中的value值private static final class IntegerWrapper {private int value;public IntegerWrapper() {}public IntegerWrapper(int value) {this.value = value;}public void decrement() {this.value--;}}
}

下面以1个实际例子说明:

假设3台服务器A、B、C的权重分别为[5, 2, 1],则总权重为5+2+1=8第1次请求 mod=0%8=0 进行0次递减 权重最后变为[[5], 2, 1] 5>0 返回A
第2次请求 mod=1%8=1 进行1次递减 权重最后变为[4, [2], 1] 2>0 返回B
第3次请求 mod=2%8=2 进行2次递减 权重最后变为[4, 1, [1]] 1>0 返回C
第4次请求 mod=3%8=3 进行3次递减 权重最后变为[[4], 1, 0] 3>0 返回A
第5次请求 mod=4%8=4 进行4次递减 权重最后变为[3, [1], 0] 1>0 返回B
第6次请求 mod=5%8=5 进行5次递减 权重最后变为[[3], 0, 0] 3>0 返回A
第7次请求 mod=6%8=6 进行6次递减 权重最后变为[[2], 0, 0] 2>0 返回A
第8次请求 mod=7%8=7 进行7次递减 权重最后变为[[1], 0, 0] 1>0 返回A
第1次请求 mod=8%8=0 进行0次递减 权重最后变为[[5], 2, 1] 5>0 返回A
...

该方法需要在mod == 0 && v.getValue() > 0 条件成立的情况下才会被返回相应的服务器。假如mod很大,比如10000,50000,甚至更大时,select方法需要进行很多次计算才能将mod减为0。由此可知,select的效率与mod有关,时间复杂度为O(mod)。mod又受最大权重maxWeight的影响,因此当某个服务提供者配置了非常大的权重,此时该方法会产生比较严重的性能问题。

双层遍历轮询

所谓双层遍历,外层为0->最大权重,内层为0->服务器个数。

public class WeightedRoundRobinLoadBalance2 implements LoadBalance{public static final String NAME = "roundrobin";private AtomicInteger atomicInteger = new AtomicInteger(0);private AtomicInteger indexSeq = new AtomicInteger(0);@Overridepublic ServerInfo select(List<ServerInfo> serverInfos) {int maxWeight = 0;int minWeight = 0;for(ServerInfo serverInfo: serverInfos){maxWeight = Math.max(maxWeight, serverInfo.getWeight());minWeight = Math.min(minWeight, serverInfo.getWeight());}if(minWeight < maxWeight && minWeight >= 0){while(true){int index = indexSeq.getAndIncrement() % serverInfos.size();if(index == 0){atomicInteger.getAndIncrement();}int currentWeight = atomicInteger.get() % maxWeight;if(serverInfos.get(index).getWeight() > currentWeight){return serverInfos.get(index);}}}return serverInfos.get(atomicInteger.getAndIncrement() % serverInfos.size());}
}

下面举例说明:

假设服务器 [A, B, C] 对应权重 [5, 2, 1]。第一轮循环,currentWeight = 1,权重大于1的有A和B,从左向右依次返回A、B第二轮循环,currentWeight = 2,权重大于2的仅有A,直接返回A第三轮循环,currentWeight = 3,权重大于3的仅有A,直接返回A第四轮循环,currentWeight = 4,权重大于4的仅有A,直接返回A第五轮循环,currentWeight = 0,权重大于0的有A、B和C,从左向右依次返回 A, B, C

该负载均衡器需要维护2个状态变量,sequence和indexSeq,sequence用于外层遍历,当indexSeq值为0,则sequence执行累加操作。

双层遍历轮询仍存在问题,在某些情况下选出的服务器序列不够均匀。比如,服务器 [A, B, C] 对应权重 [5, 1, 1]。进行7次负载均衡后,选择出来的序列为 [A, A, A, A, A, B, C]。前5个请求全部都落在了服务器 A上,这将会使服务器 A 短时间内接收大量的请求,压力陡增。而 B 和 C 此时无请求,处于空闲状态。而我们期望的结果是这样的 [A, A, B, A, C, A, A],不同服务器可以穿插获取请求。

平滑加权轮询负载均衡

Nginx 的平滑加权轮询负载均衡。每个服务器对应两个权重,分别为 weight 和 currentWeight。其中 weight 是固定的,currentWeight 会动态调整,初始值为0。当有新的请求进来时,遍历服务器列表,让它的 currentWeight 加上自身权重。遍历完成后,找到最大的 currentWeight,并将其减去权重总和,然后返回相应的服务器即可。

上面描述不是很好理解,下面还是举例进行说明。这里仍然使用服务器 [A, B, C] 对应权重 [5, 1, 1] 的例子说明,现在有7个请求依次进入负载均衡逻辑,选择过程如下:

请求编号 currentWeight 数组 选择结果 减去权重总和后的 currentWeight 数组
1 [5, 1, 1] A [-2, 1, 1]
2 [3, 2, 2] A [-4, 2, 2]
3 [1, 3, 3] B [1, -4, 3]
4 [6, -3, 4] A [-1, -3, 4]
5 [4, -2, 5] C [4, -2, -2]
6 [9, -1, -1] A [2, -1, -1]
7 [7, 0, 0] A [0, 0, 0]

如上,经过平滑性处理后,得到的服务器序列为 [A, A, B, A, C, A, A],相比之前的序列 [A, A, A, A, A, B, C],分布性要好一些。初始情况下 currentWeight = [0, 0, 0],第7个请求处理完后,currentWeight 再次变为 [0, 0, 0]。

以上就是平滑加权轮询的计算过程,接下来,我们来看看如何实现上面的计算过程的。

public class WeightedRoundRobinLoadBalance3  implements LoadBalance{public static final String NAME = "roundrobin";private Map<ServerInfo, WeightedRoundRobin> map = new ConcurrentHashMap<>();@Overridepublic ServerInfo select(List<ServerInfo> serverInfos) {int maxWeight = 0;int minWeight = 0;int totalWeight = 0;for(ServerInfo serverInfo: serverInfos){int weight = serverInfo.getWeight();maxWeight = Math.max(maxWeight, weight);minWeight = Math.min(minWeight, weight);totalWeight += weight;}int maxCurrentWeight = 0;ServerInfo selectedServerInfo = null;WeightedRoundRobin selectedWeightedRoundRobin = null;for(ServerInfo serverInfo: serverInfos){int weight = serverInfo.getWeight();if(map.get(serverInfo) == null){map.put(serverInfo, new WeightedRoundRobin(weight, 0));}WeightedRoundRobin weightedRoundRobin = map.get(serverInfo);weightedRoundRobin.setCurrent(weightedRoundRobin.getCurrent() + weightedRoundRobin.getWeight());if(weightedRoundRobin.getCurrent() > maxCurrentWeight){maxCurrentWeight = weightedRoundRobin.getCurrent();// 更新选中的selectedServerInfo = serverInfo;selectedWeightedRoundRobin = weightedRoundRobin;}}selectedWeightedRoundRobin.setCurrent(selectedWeightedRoundRobin.getCurrent() - totalWeight);return selectedServerInfo;}private class WeightedRoundRobin{private int weight;private int current;public WeightedRoundRobin() {}public WeightedRoundRobin(int weight, int current) {this.weight = weight;this.current = current;}public int getWeight() {return weight;}public void setWeight(int weight) {this.weight = weight;}public int getCurrent() {return current;}public void setCurrent(int current) {this.current = current;}@Overridepublic String toString() {return "WeightedRoundRobin{" +"weight=" + weight +", current=" + current +'}';}}
}

负载均衡算法1--轮询相关推荐

  1. 负载均衡算法--加权轮询法(Weight Round Robin)

    接上一篇博文:负载均衡算法–轮询法(Round Robin),本文讲解加权轮询算法. 加权轮询算法:不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同.给配置高.负载低 ...

  2. 负载均衡算法-权重轮询均衡

    ②权重轮询均衡(Weighted Round Robin):根据服务器的不同处理能力,给每个服务器分配不同的权值,使其能够接受相应权值数的服务请求.

  3. 加权轮询算法PHP,PHP实现负载均衡的加权轮询方法分析

    本文实例讲述了PHP实现负载均衡的加权轮询方法.分享给大家供大家参考,具体如下: 1. 负载均衡算法有哪些? 轮询法:将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务 ...

  4. 负载策略_面试官:讲一下什么是负载均衡,什么是轮询策略随机策略哈希策略

    什么是负载均衡? 先举个例子吧.以超市收银为例,假设现在只有一个窗口.一个收银员: 一般情况下,收银员平均 2 分钟服务一位顾客,10 分钟可以服务 5 位顾客:到周末高峰期时,收银员加快收银,平均 ...

  5. js轮询导致服务器瘫痪_面试官:讲一下什么是负载均衡,什么是轮询策略随机策略哈希策略...

    什么是负载均衡? 先举个例子吧.以超市收银为例,假设现在只有一个窗口.一个收银员: 一般情况下,收银员平均 2 分钟服务一位顾客,10 分钟可以服务 5 位顾客:到周末高峰期时,收银员加快收银,平均 ...

  6. 负载均衡之加权轮询算法

    在介绍加权轮询算法(WeightedRound-Robin)之前,首先介绍一下轮询算法(Round-Robin). 一:轮询算法(Round-Robin) 轮询算法是最简单的一种负载均衡算法.它的原理 ...

  7. php管理智能dns,负载均衡之DNS轮询

    域名注册商都支持对统一主机添加多条A记录,这就是DNS轮询,DNS服务器将解析请求按照A记录的顺序,随机分配到不同的IP上,这样就完成了简单的负载均衡.下图的例子是:有3台联通服务器.3台电信服务器, ...

  8. nginx负载均衡之加权轮询

    当nginx作为代理服务器时,需要将客户端的请求转发给后端服务器进行处理,如果后端服务器有多台,那如何选择合适的后端服务器来处理当前请求,也就是本篇文章要介绍的内容.nginx尽可能的把请求分摊到各个 ...

  9. 负载均衡之DNS轮询

    大多数域名注册商都支持对统一主机添加多条A记录,这就是DNS轮询,DNS服务器将解析请求按照A记录的顺序,随机分配到不同的IP上,这样就完成了简单的负载均衡.下图的例子是:有3台联通服务器.3台电信服 ...

  10. Nginx负载均衡策略之轮询与加权轮询

    轮询 是upstream模块负载均衡默认的策略.每个请求会按时间顺序逐个分配到不同的后端服务器.轮询不需要额外的配置. upstream backend{server 192.168.200.146: ...

最新文章

  1. C#精髓 第四讲 GridView 72般绝技
  2. 年中盘点:2021年最炙手可热的10家AI初创公司
  3. SpringBoot - 优雅的实现【异步编程】
  4. BZOJ-2298|区间dp|线段树
  5. UI复习练习_优酷布局
  6. C++ 如何用创建txt文件,并且写入内容(汇总)
  7. Mercurial(Hg)基本操作
  8. java源代码保存在扩展名为,看完跪了
  9. python顺序结构代码_Python代码结构——顺序、分支、循环
  10. ad17 pcb扇孔_PCB设计中为什么需要先进行扇孔
  11. 选手及评委素材信息规范处理说明
  12. oracle数据库下载地址
  13. 【经典源码】网络安全--远程控制--Gh0st3.6饭客网络sid版
  14. 瑞星千万巨款贿赂官员 制造冤狱铲除竞争对手图片
  15. SQL Server DMVs in Action 学习笔记
  16. [Unity]寻路导航
  17. 自我激励的100种方法
  18. Java爬虫之jsoup的使用
  19. 160429 vue.js 2 台灣小凡(体验 vuejs 2之随笔)
  20. C++websocket使用总结

热门文章

  1. JS中的强制类型转换
  2. js 传入字符串,转换成日期类型,如果转换失败返回null
  3. 微信(基于auto.js)自动打卡脚本
  4. webserveice搭建
  5. java基础学习--final
  6. sheepdog--介绍
  7. 精选69套中国水墨风PPT模板,无套路免费领取,需要赶紧带走
  8. 人工智能实验:动物识别系统(C++代码实现)
  9. 为什么绘声绘影10下载好后用不了
  10. 200G绘声绘影素材以及教学视频送给你