一、随机算法

1. 简单随机算法

思路:获取随机数,随机数不大于服务集合的容量,将该随机数当作下标,获取IP

// 服务IP集合
private static List<String> serverIps = Arrays.asList("A", "B", "C", "D", "E");

public static String random(){
    Random random = new Random();
    int i = random.nextInt(serverIps.size());
    return serverIps.get(i);
}

2. 加权随机算法

思路:创建一个新的List,将含有权重值得IP重新存入集合,权重是多少,就存多少个,然后使用随机算法从改集合中获取一个IP

public static String random() {
    // 含有权重的服务IP集合
    Map<String, Integer> map = new HashMap<>();
    map.put("A", 4);
    map.put("B", 6);
    map.put("C", 3);
    map.put("D", 2);
    map.put("E", 5);

    List<String> ips = new ArrayList<>();
    // 遍历map
    Set<Map.Entry<String, Integer>> entries = map.entrySet();
    for (Map.Entry<String, Integer> entry : entries) {
        // 服务IP
        String ip = entry.getKey();
        // 权重值
        Integer integer = entry.getValue();
        // 循环添加IP
        for (int i = 0; i<integer; i++){
            ips.add(ip);
        }
    }
    Random random = new Random();
    int i = random.nextInt(ips.size());
    return ips.get(i);
}

3.加权随机算法(优化)

思路:算法2中如果权重值过大,新建的List值就会很多,严重消耗内存,因此通过比较大小的方式确定随机数落在那个服务上;将各个服务的权重值看做数轴上的点,通过判断随机数落在那个区间内,从而确定选哪个服务

public static String random() {
    // 含有权重的服务IP集合
    Map<String, Integer> map = new HashMap<>();
    map.put("A", 4);
    map.put("B", 6);
    map.put("C", 3);
    map.put("D", 2);
    map.put("E", 5);

    // 总权重
    Integer totalWeigh = 0;
    Collection<Integer> values = map.values();
    for (Integer value : values) {
        totalWeigh += value;
    }
    // 获取随机数
    Random random = new Random();
    Integer pos = random.nextInt(totalWeigh);

    // 遍历含权重的服务集合
    for (String ip : map.keySet()) {
        // 获取IP权重值
        Integer integer = map.get(ip);

        // 如果随机数小于权重值,说明改随机数落在改服务上
        if (pos < integer) {
            return ip;
        }
        pos = pos - integer;
    }
    return "";
}

二、轮询算法

1. 简单轮询

思路1:设置全局下标,每次请求都+1

// 服务IP集合
    private static List<String> serverIps = Arrays.asList("A", "B", "C", "D", "E");
    // 轮询下标
    private static int pos = 0;

    public static String roundRobin() {
        if (pos >= serverIps.size()) {
            pos = 0;
        }
        String ip = serverIps.get(pos);
        pos++;
        return ip;
    }

思路2:通过取模,获取下标

// 服务IP集合
private static List<String> serverIps = Arrays.asList("A", "B", "C", "D", "E");
// 原子计数器
private static AtomicInteger atomicNum = new AtomicInteger(0);
    
public static String roundRobin() {
    int num = atomicNum.getAndAdd(1);
    int inx = num % serverIps.length;
    return serverIps[inx];
}

2. 加权轮询(List算法)

思路:创建新List,根据权重值将IP添加到新List中,然后通过简单轮询获取IP。
缺点:当权重值过大时,list过大,严重消耗内存

// 轮询下标
private static int pos = 0;

public static String roundRobin() {
    // 含有权重的服务IP集合
    Map<String, Integer> map = new HashMap<>();
    map.put("A", 4);
    map.put("B", 6);
    map.put("C", 3);
    map.put("D", 2);
    map.put("E", 5);

    List<String> ipList = new ArrayList<>();
    // 遍历map
    Set<Map.Entry<String, Integer>> entries = map.entrySet();
    for (Map.Entry<String, Integer> entry : entries) {
        // 服务IP
        String ip = entry.getKey();
        // 权重值
        Integer integer = entry.getValue();
        // 循环添加IP
        for (int i = 0; i < integer; i++) {
            ipList.add(ip);
        }
    }
    if (pos >= ipList.size()) {
        pos = 0;
    }
    String ip = ipList.get(pos);
    pos++;
    return ip;
}

3.加权轮询(取模坐标轴算法)

思路:记录请求次数,用请求次数模上总权重,然后判断该值落在坐标轴上的哪个范围

/**
 * 请求计数器
 */
private static AtomicInteger atomicNum = new AtomicInteger(0);

public static String roundRobin() {
    // 含有权重的服务IP集合
    Map<String, Integer> map = new HashMap<>();
    map.put("A", 4);
    map.put("B", 6);
    map.put("C", 3);
    map.put("D", 2);
    map.put("E", 5);
    // 获取总权重
    Integer totalWeigh = 0;
    Collection<Integer> values = map.values();
    for (Integer value : values) {
        totalWeigh += value;
    }
    // 获取请求计数器值
    int pos = atomicNum.getAndAdd(1);
    // 取模
    int inx = pos % totalWeigh;
    // 循环判断,如果余数小于IP的权重值,就返回IP
    for (String ip : map.keySet()) {
        Integer weigh = map.get(ip);
        if (inx < weigh){
            return ip;
        }
        inx = inx - weigh;
    }
    return "";
}

4. 平滑加权轮询算法

思路:引入静态权重、动态权重概念

假设现在有3台服务:

服务器 | 权重 | 动态权重
—|--- | —
A | 5 | 0
B | 1 | 0
C | 1 | 0

  • IP
  • weigh --静态权重
  • currentWeigh --动态权重
算法介绍:

说明

算法公式

1. 计算总权重totalWeigh

totalWeigh = weigh + … + weigh

2.计算currentWeigh值,初始为0

currentWeigh = currentWeigh + weigh

3.获取currentweigh最大值

MAX(currentWeigh )

4.返回IP

返回currentweigh最大值对应的服务IP

5.修改最大权重值 currentWeigh - 总权重,其它不变

MAX(currentWeigh ) - totalWeigh

运行结果:

运行次数

计算currentWeigh值

获取currentweigh最大值

返回IP

修改最大currentWeigh

1

5,1,1

5

A

-2,1,1

2

3,2,2

3

A

-4,2,2

3

1,3,3

3

B

1,-4,3

4

6,-3,4

6

A

-1,-3,4

5

4,-2,5

5

C

4,-2,-2

6

9,-1,-1

9

A

2,-1,-1

7

7,0,0

7

A

0,0,0

代码实例

  • 设置服务集合
/**
 * 服务
 */
public class ServerIp {
    // 含有权重的服务IP集合
    public static Map<String, Integer> serverMap = new HashMap<>();
    static {
        serverMap.put("A", 5);
        serverMap.put("B", 1);
        serverMap.put("C", 1);
    }
}
  • 封装权重对象
/**
 *  权重对象
 */
public class Weigh {

    /**
     *  服务IP
     */
    private String ip;
    /**
     * 静态权重
     */
    private Integer weigh;
    /**
     * 动态权重
     */
    private Integer currentWeigh;

    public String getIp() {
        return ip;
    }

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

    public Integer getWeigh() {
        return weigh;
    }

    public void setWeigh(Integer weigh) {
        this.weigh = weigh;
    }

    public Integer getCurrentWeigh() {
        return currentWeigh;
    }

    public void setCurrentWeigh(Integer currentWeigh) {
        this.currentWeigh = currentWeigh;
    }

    public Weigh(String ip, Integer weigh, Integer currentWeigh) {
        this.ip = ip;
        this.weigh = weigh;
        this.currentWeigh = currentWeigh;
    }
  • 负载算法
public class RoundRobin {

    // 服务集合
    private static List<Weigh> weighList = new ArrayList<>();

    // 总权重
    private static Integer totalWeigh = 0;

    public static String getServer() {
        // 1、初始化Weigh对象,currentWeigh值设置为0
        if (weighList.isEmpty()) {
            ServerIp.serverMap.forEach((ip, weigh) -> {
                Weigh w = new Weigh(ip, weigh, 0);
                weighList.add(w);
                totalWeigh +=weigh;
            });
        }
        // 2、循环计算currentWeigh值,公式:currentWeigh = weigh + currentWeigh
        for (Weigh weigh : weighList) {
            weigh.setCurrentWeigh(weigh.getWeigh() + weigh.getCurrentWeigh());
        }
        // 3、获取currentweigh最大值
        Weigh maxWeigh = null;
        for (Weigh weigh : weighList) {
            if (maxWeigh == null || weigh.getCurrentWeigh() > maxWeigh.getCurrentWeigh()) {
                maxWeigh = weigh;
            }
        }
        // 修改最大权重值,公式:currentWeigh - 总权重
        maxWeigh.setCurrentWeigh(maxWeigh.getCurrentWeigh() - totalWeigh);
        return maxWeigh.getIp();
    }
}