java按照权重比列分配任务实现

题目:

有三个处理服务器A,B,C,权重比为2:3:5,要求根据权重比进行分配任务

方案:

1、随机权重分配

根据权重比列,随机生成区间数量来分配,这种是相对概率,分配量不是百分百按权重来

实现方式:
新建一个数组,数组的大小是权重和或权重和的倍数,简单点我们直接设置大小为题目的2+3+5=10,数组中A,B,C的数量就是他们的权重比,2:3:5,既数组为[A,A,B,B,B,C,C,C,C,C],当然里面摆放位置可以调整(但是这种调整位置方式是否可以达到相对平滑分配的效果没试过),由在10内随机生成数作为索引值,来决定取数组中的哪个索引中的值,
因为数组内的每个服务器数量是按照权重来的,所以只要随机次数足够大,取得的服务器概率理论上是无限接近于权重比的。

2、权重分配

加权轮询,分为两种。
(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<ServerNode> serverNodeList;
    private int index;
    private final int weightTotal;

    public WeightedRobinServiceImpl(List<ServerNode> 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<String> 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<String> 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<ServerNode> 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<String> arr = new ArrayList<>();
        List<CompletableFuture<String>> threadArr = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            CompletableFuture<String> 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<String, Long> collect = arr.stream().collect(Collectors.groupingBy(s -> s, Collectors.counting()));
        collect.forEach((key, value) -> {
            System.out.println("处理服务器ip: " + key + ", 次数为:" + value);
        });
    }

    @Test
    void weightedRobinServiceTest() {
        List<ServerNode> 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<String> 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<String, Long> collect = arr.stream().collect(Collectors.groupingBy(s -> s, Collectors.counting()));
                collect.forEach((key, value) -> {
                    System.out.println("处理服务器ip: " + key + ", 次数为:" + value);
                });
                System.out.println("---------------------------------------------");
            }
        }
    }

------------------平滑加权轮询--------------------------
结果是相对分散的
![平滑轮询](https://img-blog.csdnimg.cn/9b65f75d91e64972ae1b823eb71c5139.png

------------------普通加权--------------------------
他的结果是集中的
在这里插入图片描述
------------------随机加权--------------------------
在这里插入图片描述

参考:
https://blog.csdn.net/loveyour_1314/article/details/121781952
https://my.oschina.net/u/4047016/blog/4420117

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值