有三个处理服务器A,B,C,权重比为2:3:5,要求根据权重比进行分配任务
根据权重比列,随机生成区间数量来分配,这种是相对概率,分配量不是百分百按权重来
实现方式:
新建一个数组,数组的大小是权重和或权重和的倍数,简单点我们直接设置大小为题目的2+3+5=10,数组中A,B,C的数量就是他们的权重比,2:3:5,既数组为[A,A,B,B,B,C,C,C,C,C],当然里面摆放位置可以调整(但是这种调整位置方式是否可以达到相对平滑分配的效果没试过),由在10内随机生成数作为索引值,来决定取数组中的哪个索引中的值,
因为数组内的每个服务器数量是按照权重来的,所以只要随机次数足够大,取得的服务器概率理论上是无限接近于权重比的。
加权轮询,分为两种。
(1)一种是普通的
保证任务总数内的任务是按权重比进行分配的,但是可能会导致大量的任务在短时间内分配给一台。比如10个任务,A分配2个,B分配3个,C分配5个,但是在顺序上可能C的5个在一起,然后前面5个任务全部给了C,A和B一个都没,只有当任务第六个的时候才会分配给A或B
可以通过调整数组A,B,C的位置让其分散些,如[A,B,C,A,B,C,B,C,C,C],可以让数组分配相对的均衡些。(个人推荐用这个)
实现方式:
就是生成一个大小为权重总和的服务器List,里面每个服务器的元素数量由权重值决定,每次获取索引都加一,如果满了就重置从0开始
(2)一种是平滑加权轮询
保证任务总数内的任务是按权重比进行分配的,且顺序上比较均衡,比如10个任务,A分配2个,B分配3个,C分配5个,第一个是C,第二个是B,第三个是A,再一直往下。
实现方式:
所谓权重,可以理解为能力大小不均衡,从数字角度来看,就是每次递增的大小不一,递减大小一样,那次操作每个服务器的【当前权重】都自增【权重值】(初始固定值),取当前权重最大的一个,被选中则当前权重要减去【权重总和】(初始固定值)。
import java.util.*;
/**
* 轮询接口实现类
*
* @author ppp
* @date 2022/6/21
*/
public class WeightedRobinServiceImpl {
private final List serverNodeList;
private int index;
private final int weightTotal;
public WeightedRobinServiceImpl(List list) {
this.index = 0;
this.serverNodeList = list;
this.weightTotal = list.stream().mapToInt(ServerNode::getWeight).sum();
}
/**
* 平滑加权选择
*
* @return 服务器ip
*/
public synchronized String smoothSelectServer() {
if (serverNodeList.isEmpty()) {
return null;
}
ServerNode maxServerNode = serverNodeList.stream().max(Comparator.comparingInt(ServerNode::getCurrentWeight)).get();
serverNodeList.forEach(serverNode -> {
/ * 参考其它文章的话,节点还有一个偏移权重属性,初始值为权重值,这个偏移权重是用来实时调整服务器能力的,
* 每次选举的时候,当前权重就要加上偏移权重,每次选择了这个服务器,但是这个服务器响应失败(比如请求堵塞、网络波动),那么就把他的偏移值减一
* 相反,选择这个服务器响应成功(说明服务器响应能力变好或至少没变差),那么他的偏移值就加1,但是不能超过权重值
* 我们这里测试模拟的肯定没有响应失败的情况,所以不要偏移权重字段,直接也是加的权重值(固定,标识响应能力一直不变)
* /
serverNode.setCurrentWeight(serverNode.getCurrentWeight() + serverNode.getWeight());
if (serverNode.equals(maxServerNode)) {
serverNode.setCurrentWeight(serverNode.getCurrentWeight() - weightTotal);
}
});
return maxServerNode.getIp();
}
/**
* 普通加权选择
*
* @return 服务器ip
*/
public synchronized String selectServer() {
if (serverNodeList.isEmpty()) {
return null;
}
List nodeList = new ArrayList<>();
serverNodeList.forEach(serverNode -> {
Integer weight = serverNode.getWeight();
for (int i = 0; i < weight; i++) {
nodeList.add(serverNode.getIp());
}
});
if (index >= nodeList.size()) {
index = 0;
}
String s = nodeList.get(index);
index++;
return s;
}
/**
* 随机加权选择
*
* @return 服务器ip
*/
public synchronized String randomSelectServer() {
if (serverNodeList.isEmpty()) {
return null;
}
List nodeList = new ArrayList<>();
serverNodeList.forEach(serverNode -> {
Integer weight = serverNode.getWeight();
for (int i = 0; i < weight; i++) {
nodeList.add(serverNode.getIp());
}
});
Random random = new Random();
// 在权重范围内随机
int n = random.nextInt(weightTotal);
return nodeList.get(n);
}
}
/**
* 服务节点
* @author ppp
* @date 2022/6/21
*/
public class ServerNode {
/**
* 服务地址
*/
private String ip;
/**
* 权重
*/
private Integer weight;
/**
* 当前权重
*/
private Integer currentWeight;
public ServerNode(String ip, Integer weight) {
this.ip = ip;
this.weight = weight;
this.currentWeight = weight;
}
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;
}
@Override
public String toString() {
return "ServerNode{" +
"ip='" + ip + '\'' +
", weight=" + weight +
", currentWeight=" + currentWeight +
'}';
}
}
@Test
void weightedServiceTest() throws ExecutionException, InterruptedException {
List list = new ArrayList<>();
list.add(new ServerNode("192.168.0.1", 2));
list.add(new ServerNode("192.168.0.2", 3));
list.add(new ServerNode("192.168.0.3", 5));
WeightedRobinServiceImpl weightedRobinService = new WeightedRobinServiceImpl(list);
ExecutorService executorService = Executors.newFixedThreadPool(10);
List arr = new ArrayList<>();
List> threadArr = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CompletableFuture stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
// String s = weightedRobinService.randomSelectServer();
String s = weightedRobinService.selectServer();
System.out.println("线程" + Thread.currentThread().getName() + "获得了处理器: " + s);
return s;
}, executorService);
threadArr.add(stringCompletableFuture);
}
threadArr.forEach(stringCompletableFuture -> {
try {
String s = stringCompletableFuture.get();
arr.add(s);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
Map collect = arr.stream().collect(Collectors.groupingBy(s -> s, Collectors.counting()));
collect.forEach((key, value) -> {
System.out.println("处理服务器ip: " + key + ", 次数为:" + value);
});
}
@Test
void weightedRobinServiceTest() {
List list = new ArrayList<>();
list.add(new ServerNode("192.168.0.1", 2));
list.add(new ServerNode("192.168.0.2", 3));
list.add(new ServerNode("192.168.0.3", 5));
WeightedRobinServiceImpl weightedRobinService = new WeightedRobinServiceImpl(list);
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请输入需要处理的数量: ");
if (scanner.hasNext()) {
List arr = new ArrayList<>();
int amount = scanner.nextInt();
for (int i = 0; i < amount; i++) {
// String s = weightedRobinService.smoothSelectServer();
String s = weightedRobinService.selectServer();
// String s = weightedRobinService.randomSelectServer();
System.out.println(s);
arr.add(s);
}
Map collect = arr.stream().collect(Collectors.groupingBy(s -> s, Collectors.counting()));
collect.forEach((key, value) -> {
System.out.println("处理服务器ip: " + key + ", 次数为:" + value);
});
System.out.println("---------------------------------------------");
}
}
}
------------------平滑加权轮询--------------------------
结果是相对分散的
------------------普通加权--------------------------
他的结果是集中的
------------------随机加权--------------------------
参考:
https://blog.csdn.net/loveyour_1314/article/details/121781952
https://my.oschina.net/u/4047016/blog/4420117