负载均衡--平滑加权轮询算法(Smooth Weight Round Robin)

对于加权轮询算法,从宏观角度讲,权重高的服务器被访问的次数高一些,整体近似均衡;从微观角度讲,权重高的服务器会被连续访问到,局部没有那么均衡。为了更好地解决均衡问题,nginx 的作者提出了均衡加权轮询算法。

一、算法描述

假设有N台服务器 S = {S0, S1, S2, …, Sn},默认权重为 W = {W0, W1, W2, …, Wn},当前权重为 CW = {CW0, CW1, CW2, …, CWn}。在该算法中有两个权重,默认权重表示服务器的原始权重,当前权重表示每次访问后重新计算的权重,当前权重的初始值为默认权重值,当前权重值最大的服务器为 maxWeightServer,所有默认权重之和为 originalWeightSum,服务器列表为 serverList,算法可以描述为:
1、找出当前权重值最大的服务器 maxWeightServer。
2、计算 {W0, W1, W2, …, Wn} 所有默认权重之和 originalWeightSum。
3、将 maxWeightServer.CW = maxWeightServer.CW - originalWeightSum。
4、重新计算 {S0, S1, S2, …, Sn} 的当前权重 CW,计算公式为 Sn.CW = Sn.CW + Sn.Wn。
5、返回 maxWeightServer。

假定我们现在有如下5台服务器,如下所示:

服务器地址默认权重当前权重
192.168.1.11010
192.168.1.22020
192.168.1.33030
192.168.1.43030

192.168.1.5

00

在下面的代码示例中,以这5台服务器为例进行演示。

二、java代码实现示例

package com.test.mvp.schedulealgothrim;

import java.io.Serializable;

import java.util.Map;
import java.util.TreeMap;

import java.util.*;

import com.google.common.collect.SortedMultiset;
import com.google.common.collect.TreeMultiset;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

class SmoothWeightServer implements Serializable {
    private static final long serialVersionUID = 7246747589293111189L;

    private String server;
    private Integer originalWeight;
    private Integer currentWeight;

    public SmoothWeightServer(String server, Integer originalWeight, Integer currentWeight){
        this.server = server;
        this.originalWeight = originalWeight;
        this.currentWeight = currentWeight;
    }
    public Integer getOriginalWeight() {
        return originalWeight;
    }

    public void setOriginalWeight(Integer originalWeight) {
        this.originalWeight = originalWeight;
    }

    public Integer getCurrentWeight() {
        return currentWeight;
    }

    public void setCurrentWeight(Integer currentWeight) {
        this.currentWeight = currentWeight;
    }

    public String getServer() {
        return server;
    }

    public void setServer(String server) {
        this.server = server;
    }
}

class SmoothServerManager {
    public volatile static Map<String, SmoothWeightServer> serverMap = new TreeMap<>();

    static {
        serverMap.put("192.168.1.1", new SmoothWeightServer("192.168.1.1",10,10));
        serverMap.put("192.168.1.2", new SmoothWeightServer("192.168.1.2",20,20));
        serverMap.put("192.168.1.3", new SmoothWeightServer("192.168.1.3",30,30));
        serverMap.put("192.168.1.4", new SmoothWeightServer("192.168.1.4",40,40));
        serverMap.put("192.168.1.5", new SmoothWeightServer("192.168.1.5",0,0));
    }
}

class SmoothWeightRoundRobin {
    private Map<String, SmoothWeightServer> serverMap;

    public Map<String, SmoothWeightServer> getServerMap() {
        return serverMap;
    }

    public void setServerMap(Map<String, SmoothWeightServer> serverMap) {
        this.serverMap = serverMap;
    }

    public String getServer() {
        Map<String, SmoothWeightServer> serverMap = this.getServerMap();

        // 原始权重之和
        Integer originalWeightSum = 0;
        // 最大当前权重对象
        SmoothWeightServer maxWeightServer = null;

        // 计算最大当前权重对象,同时求原始权重之和
        Iterator<String> iterator = serverMap.keySet().iterator();
        while (iterator.hasNext()) {
            SmoothWeightServer smoothWeightServer = serverMap.get(iterator.next());
            if (smoothWeightServer != null) {
                originalWeightSum += smoothWeightServer.getOriginalWeight(); // 原始权重之和
                if ((maxWeightServer == null) && (smoothWeightServer.getOriginalWeight() > 0)) {
                    maxWeightServer = smoothWeightServer;
                }
                if ((maxWeightServer !=null ) && (smoothWeightServer.getCurrentWeight() > maxWeightServer.getCurrentWeight())) {
                    maxWeightServer = smoothWeightServer;
                }
            }
        }

        /**
         * 重新调整 currentWeight 权重:
         * maxWeightServer.currentWeight -= originalWeightSum
         * 每个 smoothWeightServer.currentWeight += smoothWeightServer.originalWeight
         */
        if (maxWeightServer == null) {
            return "";
        }
        maxWeightServer.setCurrentWeight(maxWeightServer.getCurrentWeight() - originalWeightSum);

        iterator = serverMap.keySet().iterator();
        while (iterator.hasNext()) {
            SmoothWeightServer smoothWeightServer = serverMap.get(iterator.next());
            if (smoothWeightServer != null) {
                smoothWeightServer.setCurrentWeight(smoothWeightServer.getCurrentWeight() + smoothWeightServer.getOriginalWeight());
            }
        }

        return maxWeightServer.getServer();
    }
}

public class SmoothWeightRoundRobinTest {

    public static void main(String[] args) {
        Map<String, SmoothWeightServer> serverMap = new TreeMap<>(SmoothServerManager.serverMap);
        SmoothWeightRoundRobin smoothWeightRoundRobin = new SmoothWeightRoundRobin();
        smoothWeightRoundRobin.setServerMap(serverMap);
        SortedMultiset<String> serverSet = TreeMultiset.create();
        for (int i = 0; i < 10000; i++) {
            String server = smoothWeightRoundRobin.getServer();
            if (StringUtils.isBlank(server)) {
                System.out.println("not find server, please check!" );
            } else {
                serverSet.add(server, 1);
            }

        serverMap.forEach((key, value)->{
            System.out.println(key + " count:" + serverSet.count(key) + ", original weight:" + value.getOriginalWeight());
        });
    }
}

运行结果如下所示:

192.168.1.1 count:1000, original weight:10
192.168.1.2 count:2000, original weight:20
192.168.1.3 count:3000, original weight:30
192.168.1.4 count:4000, original weight:40
192.168.1.5 count:0, original weight:0

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值