1. 程式人生 > 其它 >平滑加權輪詢演算法

平滑加權輪詢演算法

所有負載均衡的場景幾乎都會用到這個演算法:假設有2個伺服器A、B,其中A的分配權重為80,B的分配權重為20,當有5個請求過來時,A希望分到4次,B希望分到1次。

一個很自然的想法:A-A-A-A-B ,按權重順序依次分配,同時計數,每分配1次,計數減1,減到0後,再分配『次權重』的伺服器。

看上去好象也湊合能用,但如果A:B的權重是100:1,A-A...-A-...(100次後),才分到B,B要坐很長時間的冷板凳,這顯然不太好。

 

於是就有了個這個演算法,它的思路如下:

初始狀態時,配置的權重為:{A:80, B:20},然後給每個伺服器,加1個動態的當前權重(curWeight),預設為0,按以下步驟:

1、curWeight += weight (注:weight為配置的權重)

2、挑選curWeight最大的,做為本次分配的結果,然後將curWeight -= sum(weight) ,即:分到的伺服器,其動態權重- sum(配置權重)

3、開始下1次分配,分配前將每臺伺服器上的curWeight += weight(即:重複步驟1)

不斷重複上述過程即可,下面分解下具體過程:

 

weight 初始狀態:{80, 20},curWeight初始狀態:{0,0}

請求
次數
curWeight += weight max(curWeight) curWeight -= sum(weight)
1 {0,0}+{80,20} = {80,20} 80,即A {80 - 100 ,20} = {-20, 20} 
2 {-20,20}+{80,20} ={60,40} 60,即A {60-100 ,40} = {-40, 40}
3  {-40, 40}+{80,20}={40,60} 60,即B {40, 60-100} = {40, -40}
4 {40, -40}+{80,20}={120,-20} 120,即A {120-100,-20} = {20, -20}
5 {20, -20}+{80,20}={100,0} 100,即A {100-100,0} = {0,0}
  
注:所有伺服器curWeight歸0時,這一輪分配就結束,
下次又回到原點,開始輪迴

所以,最終分配的順序就是 A - A - B - A - A,比原來的A - A - A - A - B,是不是更為合理? 這個演算法巧妙的地方在於,每一輪分配完成,所有伺服器的動態權重都會歸0,回到初始狀態!另外1個優勢在於,它能讓所有權重的伺服器,儘早分配到,而非等到高權重的伺服器分配完,才輪到自己。

想想這其中的數學原理也不復雜,每次分到的伺服器,其curWeight 減掉了 配置權重的總和sum(weight),然後下次分配前,又將配置權重加回來了,所以一減一加,正好抵消。

 

理解其中的原理後,用java程式碼來實現一把:

先定義一個伺服器類: 

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ServerInfo {
 
    /**
     * 伺服器主機名
     */
    public  String hostName;
 
    /**
     * (靜態)權重
     */
    public Integer weight;
 
    /**
     * 當前動態權重
     */
    public Integer curWeight;
}

然後開幹:

/**
 * 平滑加權輪詢演算法 示例
 * by 菩提樹下的楊過 yjmyzz.cnblogs.com
 *
 * @param args
 */
public static void main(String[] args) {

    List<ServerInfo> serverList = new ArrayList<>();
    serverList.add(new ServerInfo("A", 80, 0));
    serverList.add(new ServerInfo("B", 20, 0));

    //模擬2輪請求
    for (int i = 1; i <= 10; i++) {

        int sumWeight = 0;
        int maxWeight = 0;
        ServerInfo currentServer = null;

        //找出最大的動態權重
        for (ServerInfo serverInfo : serverList) {
            serverInfo.curWeight += serverInfo.weight;
            sumWeight += serverInfo.curWeight;
            maxWeight = Math.max(maxWeight, serverInfo.curWeight);
            if (maxWeight == serverInfo.curWeight) {
                currentServer = serverInfo;
            }
        }

        //輸出本次請求的選中結果,並更新選中節點的動態權重
        currentServer.curWeight -= sumWeight;
        //實際應用時,下面這行,應該是將請求,轉發到這臺伺服器
        System.out.print(currentServer.hostName + " ");

        //(以下為輔助程式碼) 每輪結束時,輔助輸出換行
        boolean roundEnd = true;
        for (ServerInfo serverInfo : serverList) {
            if (serverInfo.curWeight != 0) {
                roundEnd = false;
            }
        }
        if (roundEnd) {
            System.out.println("");
        }
    }
}

輸出:

A A B A A
A A B A A

最後擴充套件一下,這個演算法不僅僅可用於負載均衡,很多業務系統也能用到,比如:線上客服系統,當有客人來諮詢時,系統會從空閒的客服列表裡,分配一個適合的客服來為其服務。因為客服的業務能力不同,能力強的客服可以配置更高權重,多分一些進線給他,反之新人就少分配一些,只要為客服的權重標籤設定不同的值即可。

轉載於:https://www.cnblogs.com/yjmyzz/p/15916460.html