for (int i = 0; i < weights.length; i++) {
Integer weight = (Integer) weights[i];
totalWeight += weight;
//判断权重是否都相等
if (sameWeight && i > 0 && !weight.equals(weights[i - 1])) {
sameWeight = false;
}
}
Random random = new Random();
int randomPos = random.nextInt(totalWeight);
//权重值不相等
if (!sameWeight) {
for (String ip : ServerIps.WEIGHT_LIST.keySet()) {
Integer value = ServerIps.WEIGHT_LIST.get(ip);
if (randomPos < value) {
return ip;
}
randomPos = randomPos - value;
}
}
return (String) ServerIps.WEIGHT_LIST.keySet().toArray()[new Random().nextInt(ServerIps.WEIGHT_LIST.size())];
}
public static void main(String[] args) {
//连续调用10次
for (int i = 0; i < 10; i++) {
System.out.println(getServer());
}
}
}
特点
-
加权轮询,按公约后的权重设置轮询比率,循环调用节点
-
缺点:同样存在慢的提供者累积请求的问题。
代码实现(简单的轮询算法)
public class RoundRobin {
private static Integer pos = 0;
public static String getServer() {
String ip = “”;
//pos同步
synchronized (pos) {
if (pos >= ServerIps.LIST.size()) {
pos = 0;
}
ip = ServerIps.LIST.get(pos);
pos++;
}
return ip;
}
public static void main(String[] args) {
//连续调用10次
for (int i = 0; i < 11; i++) {
System.out.println(getServer());
}
}
}
执行结果如下:
192.168.0.1
192.168.0.2
192.168.0.3
192.168.0.4
192.168.0.5
192.168.0.6
192.168.0.7
192.168.0.8
192.168.0.9
192.168.0.10
192.168.0.1
这种算法很简单,也很公平,每台服务轮流来进行服务,但是有的机器性能好,所以能者多劳,和随机算法一样,加上权重维度以后,其中一种实现方法就是复制法,复制法的缺点是,比较消耗内存。
介绍另外一种算法:这种算法引入一个概念:调用编号,比如第1次调用为1,第2次调用为2,第100次调用为100,调用编号是递增的,所以我们可以根据这个调用编号推算出服务器。
假设我们有三台服务器servers = [A, B, C],对应的权重为 weights = [ 2, 5, 1], 总权重为8,我们可以理解为有8台“服务器”,这是8台“不具有并发功能”,其中有2台为A,5台为B,1台为C,一次调用过来的时候,需要按顺序访问,比如有10次调用,那么服务器调用顺序为AABBBBBCAA,调用编号会越来越大,而服务器是固定的,所以需要把调用编号“缩小”,这里对调用编号进行取余,除数为总权重和,
比如:
-
1号调用,1%8=1;
-
2号调用,2%8=2;
-
3号调用,3%8=3;
-
8号调用,8%8=0;
-
9号调用,9%8=1;
-
100号调用,100%8=4;
我们发现调用编号可以被缩小为0-7之间的8个数字,问题是怎么根据这个8个数字找到对应的服务器呢?和我们随机算法类似,这里也可以把权重想象为一个坐标轴“0-----2-----7-----8”
-
1号调用,1%8=1,offset = 1, offset <= 2 is true,取A;
-
2号调用,2%8=2;offset = 2,offset <= 2 is true, 取A;
-
3号调用,3%8=3;offset = 3, offset <= 2 is false, offset = offset - 2, offset = 1, offset <= 5,取B
-
8号调用,8%8=0;offset = 0, 特殊情况,offset = 8,offset <= 2 is false, offset = offset - 2, offset = 6, offset <= 5 is false, offset = offset - 5, offset = 1, offset <= 1 is true, 取C;
-
9号调用,9%8=1;// …
-
100号调用,100%8=4; //…
模拟调用编号获取工具:
public class Sequence {
public static Integer num=0;
public static Integer getAndIncrement(){
return ++num;
}
}
public class WeightRoundRobin {
private static Integer pos = 0;
public static String getServer() {
int totalWeight = 0;
boolean sameWeight = true;
Object[] weights = ServerIps.WEIGHT_LIST.values().toArray();
for (int i = 0; i < weights.length; i++) {
Integer weight = (Integer) weights[i]