2、加权轮询
加权轮询还是有两种常用的实现方式,和加权随机是一样的,在这里,我就演示我认为比较好的一种:
public class WeightRound { static Servers servers = new Servers(); static int index; public static String go() { int allWeight = servers.map.values().stream().mapToInt(a -> a).sum(); int number = (index++) % allWeight; for (var item : servers.map.entrySet()) { if (item.getValue() > number) { return item.getKey(); } number -= item.getValue(); } return ""; } public static void main(String[] args) { for (var i = 0; i < 15; i++) { System.out.println(go()); } } }
运行结果:
加权轮询,看起来并没什么问题,但是还是有一点瑕疵,那就是其中一台服务器的压力可能会突然上升,而另外的服务器却很“悠闲(喝着咖啡,看着新闻)”。我们希望虽然是按照轮询,但是中间最好可以有交叉,所以出现了第三种轮询算法:平滑加权轮询。
3、平滑加权轮询
平滑加权是一个算法,很神奇的算法,我们有必要先对这个算法进行讲解。比如A服务器的权重是5,B服务器的权重是1,C服务器的权重是1。这个权重,我们称之为“固定权重”,既然这个叫“固定权重”,那么肯定还有叫“非固定权重”的,没错,“非固定权重”每次都会根据一定的规则变动。
- 第一次访问,ABC的“非固定权重”分别是 5 1 1(初始),因为5是其中最大的,5对应的就是A服务器,所以这次选到的服务器就是A,然后我们用当前被选中的服务器的权重-各个服务器的权重之和,也就是A服务器的权重-各个服务器的权重之和。也就是5-7=-2,没被选中的服务器的“非固定权重”不做变化,现在三台服务器的“非固定权重”分别是-2 1 1。
- 第二次访问,把第一次访问最后得到的“非固定权重”+“固定权重”,现在三台服务器的“非固定权重”是3,2,2,因为3是其中最大的,3对应的就是A服务器,所以这次选到的服务器就是A,然后我们用当前被选中的服务器的权重-各个服务器的权重之和,也就是A服务器的权重-各个服务器的权重之和。也就是3-7=-4,没被选中的服务器的“非固定权重”不做变化,现在三台服务器的“非固定权重”分别是-4 1 1。
- 第三次访问,把第二次访问最后得到的“非固定权重”+“固定权重”,现在三台服务器的“非固定权重”是1,3,3,这个时候3虽然是最大的,但是却出现了两个,我们选第一个,第一个3对应的就是B服务器,所以这次选到的服务器就是B,然后我们用当前被选中的服务器的权重-各个服务器的权重之和,也就是B服务器的权重-各个服务器的权重之和。也就是3-7=-4,没被选中的服务器的“非固定权重”不做变化,现在三台服务器的“非固定权重”分别是1 -4 3……以此类推,最终得到这样的表格:
当第8次的时候,“非固定权重“又回到了初始的5 1 1,是不是很神奇,也许算法还是比较绕的,但是代码却简单多了:
public class Server { public Server(int weight, int currentWeight, String ip) { this.weight = weight; this.currentWeight = currentWeight; this.ip = ip; } private int weight; private int currentWeight; private String ip; public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } public int getCurrentWeight() { return currentWeight; } public void setCurrentWeight(int currentWeight) { this.currentWeight = currentWeight; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } }
public class Servers { public HashMap<String, Server> serverMap = new HashMap<>() { { put("192.168.1.1", new Server(5,5,"192.168.1.1")); put("192.168.1.2", new Server(1,1,"192.168.1.2")); put("192.168.1.3", new Server(1,1,"192.168.1.3")); } }; }
public class SmoothWeightRound { private static Servers servers = new Servers(); public static String go() { Server maxWeightServer = null; int allWeight = servers.serverMap.values().stream().mapToInt(Server::getWeight).sum(); for (Map.Entry<String, Server> item : servers.serverMap.entrySet()) { var currentServer = item.getValue(); if (maxWeightServer == null || currentServer.getCurrentWeight() > maxWeightServer.getCurrentWeight()) { maxWeightServer = currentServer; } } assert maxWeightServer != null; maxWeightServer.setCurrentWeight(maxWeightServer.getCurrentWeight() - allWeight); for (Map.Entry<String, Server> item : servers.serverMap.entrySet()) { var currentServer = item.getValue(); currentServer.setCurrentWeight(currentServer.getCurrentWeight() + currentServer.getWeight()); } return maxWeightServer.getIp(); } public static void main(String[] args) { for (var i = 0; i < 15; i++) { System.out.println(go()); } } }
运行结果: