轮询实现负载均衡

zllbirdonland 2020-02-10

轮询

主要思想是服务器一个接一个的服务

1 简单实现

public class RoundRobin {
    private static Integer pos = 0;

    public static String getServer() {
        if(pos >= ServerIps.LIST.size()) {
            pos = 0;
        }

        String ip = ServerIps.LIST.get(pos);
        pos++;
        return ip;
    }

    public static void main(String[] args) {
        for(int i = 0; i < 20; i++) {
            System.out.println(getServer());
        }
    }

}

2 优化:考虑权重

考虑权重的时候

  1. 暴力解决,将=创建一个LIST,将IP地址按照权重的大小放入LIST里面
  2. 还是按照随机优化的思想

其中A、B、C分别代表3个IP地址,权重分别为5、3、2 A: 5 B: 3 C: 2

映射到坐标轴为
0-----5---8--10

offset的取值再不是一个随机数,而是0,1,2,3,4,5,6,7,8,9

1 ——> A
2 ——> A 
...
6 ——> B
...

代码实现

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;


public class ServerIps {

    public static final Map<String, Integer> WEIGHT_MAP = new LinkedHashMap<>();
    static {
        WEIGHT_MAP.put("192.168.0.1", 2);
        WEIGHT_MAP.put("192.168.0.2", 8);
        WEIGHT_MAP.put("192.168.0.3", 1);
        WEIGHT_MAP.put("192.168.0.4", 9);
        WEIGHT_MAP.put("192.168.0.5", 4);
        WEIGHT_MAP.put("192.168.0.6", 6);
    }

}

注意其中采用的是LinkedHashMap,这样可以保证输出的顺序和输入的顺序一致

public class RequestId {
    public static Integer num = 0;

    public static Integer getAndIncrement() {
        return num++;
    }

}

上面代码模仿的是客户的ID号,根据客户的ID号来控制服务器的轮询。

public class RoundRobin2 {
public static String getServer() {

        int totalWeight = 0;
        for (Integer weight: ServerIps.WEIGHT_MAP.values()) {
            totalWeight += weight;
        }

        int requestId = RequestId.getAndIncrement();
        int offset = requestId % totalWeight;

        for(String ip: ServerIps.WEIGHT_MAP.keySet()) {
            Integer weight = ServerIps.WEIGHT_MAP.get(ip);
            if (offset < weight) {
                return ip;
            }

            offset -= weight;
        }
        return null;

    }

    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {
            System.out.println(getServer());
        }
    }

}

3 优化:平滑加权轮询算法

Nginx默认采用这种算法

A: 5
B: 1
C: 1 
这样的话,优化1中的访问顺序为AAAAABC,这样的话对服务器A的压力比较大

如果按照离散的话,就不会有这样的问题,如下面这种顺序
AABACAA
这样不仅能使服务比较分散,也能保证权重,还能达到轮询的目的

具体过程如下:

ip
weight: 静态,5,1,1 currentWeight:动态

currentWeight: 0,0,0

currentWeight+=weightmax(currentWeight)resultmax(currentWeight)-=sum(weight)7
5,1,15A-2,1,1
3,2,23A-4,2,2
1,3,33B1,-4,3
6,-3,46A-1,-3,4
4,-2,55C4,-2,-2
9,-1,-19A2,-1,-1
7,0,07A0,0,0
5,1,1.........

代码实现

public class Weight {
    private String ip;
    private Integer weight;
    private Integer currentWeight;

    public Weight(String ip, Integer weight, Integer currentWeight) {
        super();
        this.ip = ip;
        this.weight = weight;
        this.currentWeight = currentWeight;
    }

    public String getIp() {
        return ip;
    }

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

    public Integer getWeight() {
        return weight;
    }

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

    public Integer getCurrentWeight() {
        return currentWeight;
    }

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

}

=============================

import java.util.LinkedHashMap;
import java.util.Map;

public class WeightRoundRobin {
    private static Map<String, Weight> weightMap = new LinkedHashMap<>();

    public static String getServer() {
        if(weightMap.isEmpty()) {
            for(String ip: ServerIps.WEIGHT_MAP.keySet()) {
                Integer weight = ServerIps.WEIGHT_MAP.get(ip);          
                weightMap.put(ip, new Weight(ip, weight, 0));

            }
        }
        // currentWeight += weight
        for(Weight weight: weightMap.values()) {
            weight.setCurrentWeight(weight.getCurrentWeight() + weight.getWeight());
        }

        Weight maxCurrentWeight = null;
        for (Weight weight: weightMap.values()) {
            if (maxCurrentWeight == null || weight.getCurrentWeight() > maxCurrentWeight.getCurrentWeight()) {
                maxCurrentWeight = weight;
            }
        }

        // max(currentWeight) -= sum(weight)
        int totalWeight = 0;
        for (Integer weight: ServerIps.WEIGHT_MAP.values()) {
            totalWeight += weight;
        }

        maxCurrentWeight.setCurrentWeight(maxCurrentWeight.getCurrentWeight() - totalWeight);

        //result
        return maxCurrentWeight.getIp();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println(getServer());
        }
    }
}

相关推荐