平滑加权轮询算法的实现

613 阅读2分钟

背景

这是负载均衡中的一个算法,nginx中就用到了这个。如果业务场景中有负载均衡的概念,可以使用这个算法

实践

基本概念

名称备注实例
S实例S1
WWeight 配置权重W = 5
CWCurrent Weight 当前权重CW = 1
currentPos当前位置0 (第一位)

假设有N台实例 S = {S1, S2, S3.... Sn},配置权重为W = {W1, W2, W3... Wn}, 当前有效权重为CW={CW1, CW2,CW3 ... CWn}

所有权重和为weightSum; 指示变量currentPos指向当前实例ID

步骤

  1. 初始化每个实例Si的当前有效权重CWi 为 Wi, 并计算求和weightSum
  1. 选出CW最大的实例, 将CWi - weightSum , 并将currentPos指向当前位置
  1. 把每个实例的CWi都加上Wi
  1. 此时currentPos指向的实例都是需要调度的实例
  1. 重复2,3,4步骤

初始化

实例 S权重 W
192.168.10.15
192.168.10.21
192.168.10.31

平滑加权轮询过程

请求选中前的当前权重currentPos选中的实例选中后当前权重
1{5, 1, 1}0192.168.10.1{-2, 1, 1}
2{3, 2, 2}0192.168.10.1{-4, 2, 2}
3{1, 3, 3}1192.168.10.2{1, -4, 3}
4{6, -3, 4}0192.168.10.1{-1, -3, 4}
5{4, -2, 5}2192.168.10.3{4, -2, -2}
6{9, -1, -1}0192.168.10.1{2, -1, -1}
7{7, 0, 0}0192.168.10.1{0, 0, 0}
8{5, 1, 1}0192.168.10.1{-2, 1, 1}

代码

package com.ybj.algorithm.leetcode.nginx.smoothWeightedPolling;

import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.beans.BeanUtils; import org.springframework.util.CollectionUtils;

import java.util.*; import java.util.concurrent.ConcurrentHashMap;

public class SmoothWeightedPollingDemo {

/**
 * 权重对象
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
static class weightDTO{

    /**
     * 名称
     */
    String name;

    /**
     * 权重
     */
    Integer weight;

    /**
     * 当前权重
     */
    Integer currentWeight = 0;
}

@Data
@AllArgsConstructor
static
class Percent {

    /**
     * 名称
     */
    String name;

    /**
     * 权重
     */
    Integer weight;
}


public static Map<String,weightDTO> weightDTOMap = new ConcurrentHashMap<>();

public static final List<String> list = new LinkedList<>();

/**
 * 权重总和
 */
private static Integer weightSum = 0;

/**
 * 调整权重
 * @param pages
 */
public static void adjustWeight(List<Percent> pages){
    if(CollectionUtils.isEmpty(weightDTOMap)){
        pages.forEach( a->{
            weightDTO weightDTO = new weightDTO();
            BeanUtils.copyProperties(a,weightDTO);
            //组装权重对象
            weightDTOMap.put(a.getName(),weightDTO);
            //计算权重和
            weightSum+=a.getWeight();
        });
    }
    //重置currentWeight
    weightDTOMap.values().forEach(a->{
        a.setCurrentWeight(a.getCurrentWeight() + a.getWeight());
    });

}

public static void main(String[] args) {
    //计数Map
    Map<String,Integer> countMap = new HashMap<>();
    //百分比列表
    List<Percent> percentList = new LinkedList<>();
    percentList.add( new Percent("a",5));
    percentList.add(new Percent("b",1));
    percentList.add(new Percent("c",1));
    weightDTO maxWeightDTO = null;
    for (int i = 0; i < 70; i++) {
        //调整权重
        adjustWeight(percentList);
        for(weightDTO weightDTO: weightDTOMap.values()){
            //选出最大的CW
            if(maxWeightDTO == null || maxWeightDTO.getCurrentWeight() < weightDTO.getCurrentWeight()){
                maxWeightDTO = weightDTO;
            }
        }
        if(Objects.nonNull(maxWeightDTO)){
            //重置最大实例的 CW = CW - weightSum
            maxWeightDTO.setCurrentWeight(maxWeightDTO.getCurrentWeight()-weightSum);
            final Integer count = countMap.getOrDefault(maxWeightDTO.getName(), 0);
            countMap.put(maxWeightDTO.getName(),count+1);
        }
    }
    System.out.println("countMap = " + countMap.toString());
}

}

特点

从名字中就可以得出算法的特性:

平滑

多个节点,不会只抓住一个节点薅羊毛,而是平滑的选择不同的节点

加权

可以通过初始化的权重配置完成不同的负载均衡。

参考

cloud.tencent.com/developer/a…