為什么使用平滑加權(quán)輪詢算法
分布式服務(wù)器集群環(huán)境下,每個服務(wù)處理請求的能力不一樣届惋,我們就可以根據(jù)不同服務(wù)器的處理請求能力設(shè)置權(quán)重怯晕,將請求平滑分給各個服務(wù)器進(jìn)行處理,減輕服務(wù)器的壓力
相比隨機(jī)訪問的訪問服務(wù)器方式贩据,可能因為隨即情況導(dǎo)致一個服務(wù)器承受它這個能力范圍內(nèi)不該承受的壓力,宕機(jī)風(fēng)險較大闸餐,而平滑加權(quán)輪詢算法就能大大降低這個風(fēng)險饱亮,嚴(yán)格按照權(quán)重周期性訪問,只要我們的權(quán)重設(shè)置的合理舍沙,可以極大提高系統(tǒng)的抗壓能力
代碼如下:
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 隨機(jī)訪問服務(wù)器算法
*/
public class RandomAccessToServer {
//HashMap遍歷時是無序的近上,我們使用隨機(jī)算法時需要有序遍歷
public static LinkedHashMap<String, Integer> weightMap = new LinkedHashMap<>();
//記錄某個服務(wù)器的訪問次數(shù)
static Map<String, Integer> resultMap = new HashMap<>();
//隨機(jī)訪問,存在隨機(jī)數(shù)集中時某一個服務(wù)器訪問壓力過大的問題
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) {
//記錄訪問每個服務(wù)器的次數(shù)
if (resultMap.containsKey(key)) resultMap.put(key, resultMap.get(key) + 1);
else resultMap.put(key, 1);
return key;
}
randomVal -= weight;
}
return null;
}
//每次進(jìn)行計算的中間集合
private static Map<String, Integer> midMap = new HashMap<>();
//Dubbo拂铡、Nginx 平滑加權(quán)輪詢算法:防止按權(quán)重線性輪詢情況下壹无,某權(quán)重較大的服務(wù)器訪問壓力較大
public static String getServer2() {
/**線性周期性循環(huán)
* (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;
//找到權(quán)重最大的服務(wù)器
for (Map.Entry<String, Integer> entry : midMap.entrySet()) {
int weight = entry.getValue();
if (weight > maxWeight) {
maxWeight = weight;
server = entry.getKey();
}
}
//記錄服務(wù)器訪問次數(shù)感帅,進(jìn)行統(tǒng)計
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());
}
}
//添加服務(wù)器的方法
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("每個服務(wù)器的訪問次數(shù):");
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() + ""));
}
}
}
測試結(jié)果:
image.png