【算法】负载均衡

188 阅读1分钟

1. 介绍

  负载均衡就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行。比如对于某个请求,使用负载均衡能选择合适的服务器去响应。

2. 算法实现

  自定义一个Server类

public class Server {
    private String ip;
    private int weight;
    private int currentWeight = 0;

    public Server(String ip) {
        this.ip = ip;
    }

    public Server(String ip, int weight) {
        this.ip = ip;
        this.weight = weight;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public int getWeight() {
        return weight;
    }

    public int getCurrentWeight() {
        return currentWeight;
    }

    public void setCurrentWeight(int currentWeight) {
        this.currentWeight = currentWeight;
    }

    @Override
    public String toString() {
        return "Server{" +
                "ip='" + ip + '\'' +
                ", weight=" + weight +
                '}';
    }
}

  在测试类中定义如下变量,分别模拟无权重的服务器和有权重的服务器

private static Server[] IP = {
    new Server("192.168.0.1"),
    new Server("192.168.0.2"),
    new Server("192.168.0.3"),
    new Server("192.168.0.4"),
    new Server("192.168.0.5"),
    new Server("192.168.0.6"),
    new Server("192.168.0.7"),
    new Server("192.168.0.8"),
    new Server("192.168.0.9"),
    new Server("192.168.0.10")
};

private static int TOTAL_WEIGHT = 0;
private static Server[] IP_WITH_WEIGHT = {
    new Server("192.168.0.1", 5),
    new Server("192.168.0.2", 1),
    new Server("192.168.0.3", 1)
};

static {
    for (int i = 0; i < IP_WITH_WEIGHT.length; i++) {
        TOTAL_WEIGHT += IP_WITH_WEIGHT[i].getWeight();
    }
}

2.1 随机算法

2.1.1 无权重

public static Server random() {
    Random random = new Random();
    return IP[random.nextInt(10)];
}

2.1.2 有权重

  思路如下:求出所有权重的和,随机一个数,当随机数落在权重之和的某个区间时,则返回该区间对应的IP。类似于在一条线段上随机置点,获取该点所在区间。

public static Server randomWithWeight() {
    int randomWeight = new Random().nextInt(TOTAL_WEIGHT);

    for (int i = 0; i < IP_WITH_WEIGHT.length; i++) {
        int weight = IP_WITH_WEIGHT[i].getWeight();
        if (randomWeight < weight) {
            return IP_WITH_WEIGHT[i];
        } else {
            randomWeight -= weight;
        }
    }
    return null;
}

2.2 轮询算法

2.2.1 无权重

private static int INDEX = 0;
public static Server roundRobin() {
    if (INDEX == IP.length) {
        INDEX = 0;
    }
    return IP[INDEX++];
}

2.2.2 有权重

  Nginx的默认算法。

public static Server roundRobinWithWeight() {
    // 1.所有服务器的currentWeight都加上各自的weight
    for (int i = 0; i < IP_WITH_WEIGHT.length; i++) {
        int currentWeight = IP_WITH_WEIGHT[i].getCurrentWeight();
        int weight = IP_WITH_WEIGHT[i].getWeight();
        IP_WITH_WEIGHT[i].setCurrentWeight(currentWeight + weight);
    }

    // 2.找出currentWeight最大的那个服务器,并将其currentWeight减去所有weight的和
    Server max = IP_WITH_WEIGHT[0];
    for (int i = 1; i < IP_WITH_WEIGHT.length; i++) {
        if (max.getCurrentWeight() < IP_WITH_WEIGHT[i].getCurrentWeight()) {
            max = IP_WITH_WEIGHT[i];
        }
    }
    max.setCurrentWeight(max.getCurrentWeight() - TOTAL_WEIGHT);

    return max;
}

2.3 一致性哈希算法

  该算法的目的是,同一个用户只会访问到同一台服务器,算法介绍可看这篇文章

public class ConsistentHash {

    private static Server[] IP = {
            new Server("192.168.0.1"),
            new Server("192.168.0.2"),
            new Server("192.168.0.3"),
            new Server("192.168.0.4"),
            new Server("192.168.0.5"),
            new Server("192.168.0.6"),
            new Server("192.168.0.7"),
            new Server("192.168.0.8"),
            new Server("192.168.0.9"),
            new Server("192.168.0.10")
    };

    private static TreeMap<Long, Server> virtualNode = new TreeMap<>();

    /**
     * 每个服务器虚拟节点的数量
     */
    private static final int NODE_SIZE = 100;

    static {
        for (Server server : IP) {
            for (int i = 0; i < NODE_SIZE; i++) {
                long hash = getHash(server.getIp() + i);
                virtualNode.put(hash, server);
            }
        }
    }


    public static void main(String[] args) {
        System.out.println(getServer("10.16.129.67"));
        System.out.println(getServer("100.19.129.68"));
        System.out.println(getServer("10.16.129.67"));
    }

    /**
     * 获取客户端要访问的服务器
     */
    private static Server getServer(String client) {
        long clientHash = getHash(client);

        // 获取红黑树中大于"客户端哈希值"的子树的第一个节点
        SortedMap<Long, Server> subTree = virtualNode.tailMap(clientHash);
        Long firstKey;
        if (null == subTree) {
            firstKey = virtualNode.firstKey();
        } else {
            firstKey = subTree.firstKey();
        }
        return virtualNode.get(firstKey);
    }

    /**
     * 计算哈希值
     */
    private static long getHash(String str) {
        final int p = 16777619;
        long hash = 2166136261L;

        for (int i = 0; i < str.length(); i++) {
            hash = (hash ^ str.charAt(i)) * p;
        }
        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;

        if (hash < 0) {
            hash = Math.abs(hash);
        }
        return hash;
    }
}

3. 相关链接