背景
这是负载均衡中的一个算法,nginx中就用到了这个。如果业务场景中有负载均衡的概念,可以使用这个算法
实践
基本概念
名称 | 备注 | 实例 |
---|---|---|
S | 实例 | S1 |
W | Weight 配置权重 | W = 5 |
CW | Current Weight 当前权重 | CW = 1 |
currentPos | 当前位置 | 0 (第一位) |
假设有N台实例 S = {S1, S2, S3.... Sn},配置权重为W = {W1, W2, W3... Wn}, 当前有效权重为CW={CW1, CW2,CW3 ... CWn}
所有权重和为weightSum; 指示变量currentPos指向当前实例ID
步骤
- 初始化每个实例Si的当前有效权重CWi 为 Wi, 并计算求和weightSum
- 选出CW最大的实例, 将CWi - weightSum , 并将currentPos指向当前位置
- 把每个实例的CWi都加上Wi
- 此时currentPos指向的实例都是需要调度的实例
- 重复2,3,4步骤
初始化
实例 S | 权重 W |
---|---|
192.168.10.1 | 5 |
192.168.10.2 | 1 |
192.168.10.3 | 1 |
平滑加权轮询过程
请求 | 选中前的当前权重 | currentPos | 选中的实例 | 选中后当前权重 |
---|---|---|---|---|
1 | {5, 1, 1} | 0 | 192.168.10.1 | {-2, 1, 1} |
2 | {3, 2, 2} | 0 | 192.168.10.1 | {-4, 2, 2} |
3 | {1, 3, 3} | 1 | 192.168.10.2 | {1, -4, 3} |
4 | {6, -3, 4} | 0 | 192.168.10.1 | {-1, -3, 4} |
5 | {4, -2, 5} | 2 | 192.168.10.3 | {4, -2, -2} |
6 | {9, -1, -1} | 0 | 192.168.10.1 | {2, -1, -1} |
7 | {7, 0, 0} | 0 | 192.168.10.1 | {0, 0, 0} |
8 | {5, 1, 1} | 0 | 192.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());
}
}
特点
从名字中就可以得出算法的特性:
平滑
多个节点,不会只抓住一个节点薅羊毛,而是平滑的选择不同的节点
加权
可以通过初始化的权重配置完成不同的负载均衡。