加权轮询算法:不同的后端服务器,在机器的配置和当前系统的负载方面,可能并不相同。因此,它们的抗压能力也不相同。给配置高、负载低的机器配置更高的权重,让其处理更多的请求;给配置低、负载高的机器分配较低的权重,降低系统负载。加权轮询算法能很好地处理这一问题,并将请求顺序地按照权重分配到后端服务器。
一、算法描述
假设有 N 台服务器 S = {S0, S1, S2, …, Sn},默认权重为 W = {W0, W1, W2, …, Wn},服务器列表为 serverList,算法可以描述为:
1、初始化 serverList,将 W0 个 S0 加入至serverList,将 W1 个 S1 加入至serverList,依据此规则,将所有的服务器加入至 serverList 中;
2、从 serverList 的从 S0 开始依序调度;
3、若所有服务器都已被调度过,则从头重新开始,循环调度。
假定我们现在有如下四台服务器:
服务器地址 | 权重 |
192.168.1.1 | 1 |
192.168.1.2 | 2 |
192.168.1.3 | 3 |
192.168.1.4 | 4 |
初始化服务列表后, serverList 如下:
服务器地址 | 序号 |
192.168.1.1 | 1 |
192.168.1.2 | 2 |
192.168.1.2 | 3 |
192.168.1.3 | 4 |
192.168.1.3 | 5 |
192.168.1.3 | 6 |
192.168.1.4 | 7 |
192.168.1.4 | 8 |
192.168.1.4 | 9 |
192.168.1.4 | 10 |
通过在服务器列表中增加相应权重个数的服务器,加权轮训算法实现加权效果,每个服务器会依序被轮训到。
二、java代码实现
package com.test.mvp.schedulealgothrim;
import com.google.common.collect.SortedMultiset;
import com.google.common.collect.TreeMultiset;
import org.springframework.util.CollectionUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;
class Server implements Serializable {
private static final long serialVersionUID = 7246747589293111189L;
private String server;
private Integer weight;
private String description;
public Server(String server, String description, Integer weight) {
this.server = server;
this.description = description;
this.weight = weight;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getServer() {
return server;
}
public void setServer(String server) {
this.server = server;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
}
class ServerManager {
public static Map<String, Server> serverMap = new TreeMap<>();
static {
serverMap.put("192.168.1.1", new Server("192.168.1.1", "第1台server", 1));
serverMap.put("192.168.1.2", new Server("192.168.1.2", "第2台server", 2));
serverMap.put("192.168.1.3", new Server("192.168.1.3", "第3台server", 3));
serverMap.put("192.168.1.4", new Server("192.168.1.4", "第4台server", 4));
}
}
class WeightRoundRobin {
private static AtomicInteger indexAtomic = new AtomicInteger(0);
private static ArrayList<String> middleServerList;
public static void computeMiddleServerList() {
ArrayList<String> serverList = new ArrayList<>();
Set<String> serverSet = ServerManager.serverMap.keySet();
for (String server : serverSet) {
Integer weight = ServerManager.serverMap.get(server).getWeight();
for (int i = 0; i < weight; i++) {
serverList.add(server);
}
}
middleServerList = serverList;
}
public static String getServer() {
if (CollectionUtils.isEmpty(middleServerList)) {
computeMiddleServerList();
System.out.println("compute Middle Server List");
}
if (indexAtomic.get() >= middleServerList.size()) {
indexAtomic.set(0);
}
return middleServerList.get(indexAtomic.getAndIncrement());
}
}
public class WeightRoundRobinTest {
public static void main(String[] args) {
SortedMultiset<String> serverSet = TreeMultiset.create();
for (int i = 0; i < 100; i++) {
String server = WeightRoundRobin.getServer();
Server curServer = ServerManager.serverMap.get(server);
System.out.println(server + ", " + curServer.getDescription() + ", weight=" + curServer.getWeight());
serverSet.add(server, 1);
}
ServerManager.serverMap.forEach((key, value)->{
System.out.println(key + ", count:" + serverSet.count(key));
});
}
}
运行结果如下所示:
compute Middle Server List
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, count:10
192.168.1.2, count:20
192.168.1.3, count:30
192.168.1.4, count:40
说明:
1、使用 AtomicInteger 进行轮询索引的增减,保证并发的安全性。
2、在多线程的情况下,若线程A修改 ServerManager.serverMap 的值,则线程B无法即时拿到线程A修改后的值,可能会导致请求错误,需要调用方进行容错处理。
3、从宏观的角度讲,权重高的服务器被访问的次数高一些,近似均衡;微观的角度讲,权重高的服务器会被连续访问到,局部没有那么均衡。