带权值轮询负载均衡探讨与实现

带权轮询负载均衡业务场景

负载均衡是在请求资源时,当资源有多个,我们应该请求哪一个才能让资源利用率最大化的方法。

轮询是最简单粗暴的一种策略,这种策略把每个资源都请求一遍,最大的好处是每个资源承受的请求量都一样。

而带权轮询负载均衡也是最常用的其中一种策略,它的使用场景是当多个资源可以承受的请求量不同(也就是系统的性能有差异)时,我们不应该像简单轮询一样让它们承担相同量的请求,而是针对每个资源不同的承载量去请求它们,这样才可以让每个资源被充分的利用。

接下来介绍三种实现带权轮询的方法。

取随机值法

这种方法是最不靠谱的方法🤣,但也是及其容易实现的方法,也就是取随机值,把随机值分为几个区间,每种资源对应一个区间,区间长度就表示资源对应的权值。

代码实现

public class RandomWeightLoadBalance {  
  
    // key:服务id  value:权值  
    private final static Map<Integer, Integer> services = new ConcurrentHashMap<>();  
  
    static {  
        // 初始化三个服务  
        services.put(1,1);  
        services.put(2,2);  
        services.put(3,3);  
    }  
  
    public static int loadBalance(){  
        // 计算总权值  
        int countWeight = 0;  
        for(Map.Entry<Integer,Integer> service : services.entrySet()){  
            countWeight += service.getValue();  
        }  
        // 取随机值  
        double random = Math.random() * countWeight;  
        // 分区间匹配  
        int preWeight = 0;  
        int currentWeight = 0;  
        for(Map.Entry<Integer,Integer> service : services.entrySet()){  
            currentWeight += service.getValue();  
            if(preWeight < random && random < currentWeight){  
                return service.getKey();  
            }  
            preWeight = currentWeight;  
        }  
        // 返回默认值  
        return services.entrySet().iterator().next().getKey();  
    }  
  
    public static void main(String[] args) {  
        int[] counts = new int[4];  
        for(int i=0;i<100;i++){  
            counts[loadBalance()]++;  
        }  
        System.out.println(Arrays.toString(counts));  
    }  
  
}

由于随机值无法预测,所以只能从概率上实现带权值轮询,所以这种方式显然不太靠谱,但是次数多了,随机值分布也就更均匀了。

但是这种方式的还有一个缺点就是,有可能大量的请求会集中请求同一个资源,这时随机值的不可预测性导致的。所以我们通常不会用这种方式(也就是拿出来做一下对比😂)

普通带权值轮询

普通带权轮询就比随机值的要好多了,它可以精确的保证每个资源承受的请求达到预期。

代码实现

public class WeightLoadBalance {  
  
    private static final Map<Integer, Integer> services = new ConcurrentHashMap<>();  
  
    private static int totalWeight;  
  
    static {  
        services.put(1, 1);  
        services.put(2, 3);  
        services.put(3, 2);  
        for(Map.Entry<Integer, Integer> service : services.entrySet()){  
            totalWeight += service.getValue();  
        }  
    }  
  
    private static int currentIndex = 0;  
  
    public static int loadBalance(){  
        currentIndex %= totalWeight;  
        int currentWeight = 0;  
        for(Map.Entry<Integer, Integer> service : services.entrySet()){  
            currentWeight += service.getValue();  
            if(currentIndex < currentWeight){  
                currentIndex ++;  
                return service.getKey();  
            }  
        }  
        return services.entrySet().iterator().next().getValue();  
    }  
  
    public static void main(String[] args) {  
        int[] count = new int[4];  
        for(int i=0;i<100;i++){  
            count[loadBalance()]++;  
        }  
        System.out.println(Arrays.toString(count));  
    }  
  
}

但是普通带权值轮询会有连续请求同一个资源的情况,例如:

A(权值5)、B(权值3)、C(权值2),就会出现:

A-A-A-A-A-B-B-B-C-C

连续请求了五次的A资源,请求较集中。

而平滑带权值轮询就解决了这个问题。

平滑带权值轮询

接上,由于普通带权轮询负载均衡导致的连续请求同一个服务,导致请求不能分散。而使用平滑轮询后,请求的顺序变为了这样:

A-A-B-C-A-A-B-C-A-B

这样就将请求同一资源的请求分散开了,避免大量相同的请求堆集在一起请求同一个。

实现步骤

  1. 每个资源有一个权值和当前权值,当前权值初始化为0
  2. .每次请求时每个资源的当前权值变为当前权值和权值的和(currentWeight += weight)
  3. 选择权值最大的一个资源,然后将此资源的当前权值减掉总权值(currentWeight -= total Weight(totalWeight表示原始权制的和))

代码实现

public class SmoothWeightLoadBalance {  
  
    private static final Map<Integer, Integer> services = new ConcurrentHashMap<>();  
  
    private static int totalWeight;  
  
    private static final Map<Integer, Integer> currentWeights = new ConcurrentHashMap<>();  
  
    static {  
        services.put(1, 5);  
        services.put(2, 3);  
        services.put(3, 2);  
        for(Map.Entry<Integer, Integer> service : services.entrySet()){  
            totalWeight += service.getValue();  
        }  
        currentWeights.put(1, 0);  
        currentWeights.put(2, 0);  
        currentWeights.put(3, 0);  
    }  
  
    public static int loadBalance() {  
        int serviceId = services.entrySet().iterator().next().getKey();  
        int maxWeight = 0;  
        // 找出权值最大资源  
        for(Map.Entry<Integer,Integer> service : services.entrySet()) {  
            // 计算出现有权值并更新  
            int currentWeight = currentWeights.get(service.getKey()) + service.getValue();  
            currentWeights.put(service.getKey(), currentWeight);  
            // 最大权值  
            if(currentWeight > maxWeight){  
                serviceId = service.getKey();  
                maxWeight = currentWeight;  
            }  
        }  
        // 更新最大权值  
        currentWeights.put(serviceId, currentWeights.get(serviceId) - totalWeight);  
        return serviceId;  
    }  
  
    public static void main(String[] args) {  
        int[] count = new int[4];  
        for(int i=0;i<1000;i++){  
            count[loadBalance()]++;  
        }  
        System.out.println(Arrays.toString(count));  
    }  
  
}

原文链接带权值轮询负载均衡探讨与实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值