为什么使用平滑加权轮询算法
分布式服务器集群环境下,每个服务处理请求的能力不一样,我们就可以根据不同服务器的处理请求能力设置权重,将请求平滑分给各个服务器进行处理,减轻服务器的压力
相比随机访问的访问服务器方式,可能因为随即情况导致一个服务器承受它这个能力范围内不该承受的压力,宕机风险较大,而平滑加权轮询算法就能大大降低这个风险,严格按照权重周期性访问,只要我们的权重设置的合理,可以极大提高系统的抗压能力
代码如下:
package algorithm;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
/**
* @author feihong
* @date 2020-01-03 1:23
* @description 随机访问服务器算法
*/
public class RandomAccessToServer {
//HashMap遍历时是无序的,我们使用随机算法时需要有序遍历
public static LinkedHashMap<String, Integer> weightMap = new LinkedHashMap<>();
//记录某个服务器的访问次数
static Map<String, Integer> resultMap = new HashMap<>();
//随机访问,存在随机数集中时某一个服务器访问压力过大的问题
public static String getServer() {
int totalWeight = 0;
for (int i : weightMap.values()) {
totalWeight += i;
}
int randomVal = new Random().nextInt(totalWeight);
for (String key : weightMap.keySet()) {
int weight = weightMap.get(key);
if (randomVal <= weight) {
//记录访问每个服务器的次数
if (resultMap.containsKey(key)) resultMap.put(key, resultMap.get(key) + 1);
else resultMap.put(key, 1);
return key;
}
randomVal -= weight;
}
return null;
}
//每次进行计算的中间集合
private static Map<String, Integer> midMap = new HashMap<>();
//Dubbo、Nginx 平滑加权轮询算法:防止按权重线性轮询情况下,某权重较大的服务器访问压力较大
public static String getServer2() {
/**线性周期性循环
* (1)找到midMap中最大值,减去totalWeight(7)
* (2)midMap中所有值与weightMap中的值相加
* 周期性:
* weightMap: 5, 1, 1
midMap maxVal - total server
5, 1, 1 maxWeight = 5 -2 , 1 , 1(maxWeight - totalWeight) A
3, 2, 2 maxWeight = 3 -4 , 2 , 2(maxWeight - totalWeight) A
1, 3, 3 maxWeight = 3 1 , -4 , 3(maxWeight - totalWeight) B
6, -3, 4 maxWeight = 6 -1 , -3 , 4(maxWeight - totalWeight) A
4, -2, 5 maxWeight = 5 4 , -2 ,-2(maxWeight - totalWeight) C
9, -1,-1 maxWeight = 9 2 , -1 ,-1(maxWeight - totalWeight) A
7, 0, 0 maxWeight = 7 0 , 0 , 0(maxWeight - totalWeight) A
新周期开始了
5, 1, 1 maxWeight = 5 -2 , 1 , 1(maxWeight - totalWeight)
*/
int maxWeight = 0;
String server = null;
//找到权重最大的服务器
for (Map.Entry<String, Integer> entry : midMap.entrySet()) {
int weight = entry.getValue();
if (weight > maxWeight) {
maxWeight = weight;
server = entry.getKey();
}
}
//记录服务器访问次数,进行统计
if (resultMap.containsKey(server)) resultMap.put(server, resultMap.get(server) + 1);
else resultMap.put(server, 1);
//更新midMap
midMap.put(server, midMap.get(server) - getTotalWeight());
for (Map.Entry<String, Integer> entry2 : weightMap.entrySet()) {
String key = entry2.getKey();
midMap.put(key, midMap.get(key) + weightMap.get(key));
}
return server;
}
public static int getTotalWeight() {
int totalWeight = 0;
for (int val : weightMap.values()) {
totalWeight += val;
}
return totalWeight;
}
static {
weightMap.put("A", 40);
weightMap.put("B", 10);
weightMap.put("C", 20);
weightMap.put("D", 60);
weightMap.put("E", 30);
weightMap.put("F", 15);
weightMap.put("G", 25);
initialMap();
}
//初始化midMap
public static void initialMap() {
for (Map.Entry<String, Integer> entry : weightMap.entrySet()) {
midMap.put(entry.getKey(), entry.getValue());
}
}
//添加服务器的方法
public static void addServer(String ip, int weight) {
weightMap.put(ip, weight);
}
//精确访问测试
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
getServer2();
}
System.out.println("每个服务器的访问次数:");
for (Map.Entry entry : resultMap.entrySet()) {
float percent = (Float.parseFloat(entry.getValue() + "") / 1000) * 100;
System.out.println(entry.getKey() + ":" + entry.getValue() +
";百分比: " + String.format("%.2f", percent) + "%;" +
"实际百分比:" +
Float.parseFloat((int) weightMap.get(entry.getKey()) + "") / Float.parseFloat(getTotalWeight() + ""));
}
}
}