加权轮询算法:不同的后端服务器,在机器的配置和当前系统的负载方面,可能并不相同。因此,它们的抗压能力也不相同。给配置高、负载低的机器配置更高的权重,让其处理更多的请求;给配置低、负载高的机器分配较低的权重,降低系统负载。加权轮询算法能很好地处理这一问题,并将请求顺序地按照权重分配到后端服务器。

一、算法描述

假设有 N 台服务器 S = {S0, S1, S2, …, Sn},默认权重为 W = {W0, W1, W2, …, Wn},服务器列表为 serverList,算法可以描述为:
1、初始化 serverList,将 W0 个 S0 加入至serverList,将 W1 个 S1 加入至serverList,依据此规则,将所有的服务器加入至 serverList 中;
2、从 serverList 的从 S0 开始依序调度;
3、若所有服务器都已被调度过,则从头重新开始,循环调度。

假定我们现在有如下四台服务器:

服务器地址

权重

192.168.1.1

1

192.168.1.2

2

192.168.1.3

3

192.168.1.4

4

初始化服务列表后, serverList 如下:

服务器地址

序号

192.168.1.1

1

192.168.1.2

2

192.168.1.2

3

192.168.1.3

4

192.168.1.3

5

192.168.1.3

6

192.168.1.4

7

192.168.1.4

8

192.168.1.4

9

192.168.1.4

10

通过在服务器列表中增加相应权重个数的服务器,加权轮训算法实现加权效果,每个服务器会依序被轮训到。

二、java代码实现

package com.test.mvp.schedulealgothrim;

import com.google.common.collect.SortedMultiset;
import com.google.common.collect.TreeMultiset;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;

class Server implements Serializable {
    private static final long serialVersionUID = 7246747589293111189L;

    private String server;
    private Integer weight;
    private String description;

    public Server(String server, String description, Integer weight) {
        this.server = server;
        this.description = description;
        this.weight = weight;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }


    public String getServer() {
        return server;
    }

    public void setServer(String server) {
        this.server = server;
    }

    public Integer getWeight() {
        return weight;
    }

    public void setWeight(Integer weight) {
        this.weight = weight;
    }
}

class ServerManager {
    public static Map<String, Server> serverMap = new TreeMap<>();

    static {
        serverMap.put("192.168.1.1", new Server("192.168.1.1", "第1台server", 1));
        serverMap.put("192.168.1.2", new Server("192.168.1.2", "第2台server", 2));
        serverMap.put("192.168.1.3", new Server("192.168.1.3", "第3台server", 3));
        serverMap.put("192.168.1.4", new Server("192.168.1.4", "第4台server", 4));
    }
}

class WeightRoundRobin {
    private static AtomicInteger indexAtomic = new AtomicInteger(0);
    private static ArrayList<String> middleServerList;

    public static void computeMiddleServerList() {
        ArrayList<String> serverList = new ArrayList<>();
        Set<String> serverSet = ServerManager.serverMap.keySet();
        for (String server : serverSet) {
            Integer weight = ServerManager.serverMap.get(server).getWeight();
            for (int i = 0; i < weight; i++) {
                serverList.add(server);
            }
        }
        middleServerList = serverList;
    }

    public static String getServer() {
        if (CollectionUtils.isEmpty(middleServerList)) {
            computeMiddleServerList();
            System.out.println("compute Middle Server List");
        }
        if (indexAtomic.get() >= middleServerList.size()) {
            indexAtomic.set(0);
        }
        return middleServerList.get(indexAtomic.getAndIncrement());
    }
}

public class WeightRoundRobinTest {
    public static void main(String[] args) {
        SortedMultiset<String> serverSet = TreeMultiset.create();
        for (int i = 0; i < 100; i++) {
            String server = WeightRoundRobin.getServer();
            Server curServer = ServerManager.serverMap.get(server);
            System.out.println(server + ", " + curServer.getDescription() + ", weight=" + curServer.getWeight());
            serverSet.add(server, 1);
        }

        ServerManager.serverMap.forEach((key, value)->{
            System.out.println(key + ", count:" + serverSet.count(key));
        });
    }
}

运行结果如下所示:

compute Middle Server List
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, 第1台server, weight=1
192.168.1.2, 第2台server, weight=2
192.168.1.2, 第2台server, weight=2
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.3, 第3台server, weight=3
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.4, 第4台server, weight=4
192.168.1.1, count:10
192.168.1.2, count:20
192.168.1.3, count:30
192.168.1.4, count:40

说明:

1、使用 AtomicInteger 进行轮询索引的增减,保证并发的安全性。
2、在多线程的情况下,若线程A修改 ServerManager.serverMap 的值,则线程B无法即时拿到线程A修改后的值,可能会导致请求错误,需要调用方进行容错处理。
3、从宏观的角度讲,权重高的服务器被访问的次数高一些,近似均衡;微观的角度讲,权重高的服务器会被连续访问到,局部没有那么均衡。