負載均衡算法-輪詢權重算法

服務提供者對象構建類html

public class Refer {
    private String serverName;
    /**
     * 當前使用該referer的調用數
     *
     * @return
     */
    private int activeRefererCount;

    /**
     * 連接是否可用
     */
    private boolean isAvailable;

    /**
     * 類路徑
     */
    private String serviceKey;

    /**
     * 方法名
     */
    private String method;

    /**
     * 提供權重佔比
     */
    private int weight;

    public int getActiveRefererCount() {
        return activeRefererCount;
    }

    public void setActiveRefererCount(int activeRefererCount) {
        this.activeRefererCount = activeRefererCount;
    }

    public boolean isAvailable() {
        return isAvailable;
    }

    public void setAvailable(boolean available) {
        isAvailable = available;
    }

    public String getServerName() {
        return serverName;
    }

    public void setServerName(String serverName) {
        this.serverName = serverName;
    }

    public String getServiceKey() {
        return serviceKey;
    }

    public void setServiceKey(String serviceKey) {
        this.serviceKey = serviceKey;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "Refer{" +
                "serverName=" + serverName +
                ", activeRefererCount=" + activeRefererCount +
                ", isAvailable=" + isAvailable +
                ", serviceKey='" + serviceKey + '\'' +
                ", method='" + method + '\'' +
                ", weight=" + weight +
                '}';
    }
}

方案一:輪詢權重算法實現類算法

public class RoundRobinWeightLoadBalance {

    public static List<Refer> refers = new ArrayList<Refer>();
    private static final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<>();
    private static int[] weights = new int[] {5, 1, 1};
    private static String[] names = new String[] {"A", "B", "C"};

    static {
        for (int i = 1; i < 4; i++) {
            Refer refer = new Refer();
            refer.setServerName(names[i-1]);
            refer.setActiveRefererCount(ThreadLocalRandom.current().nextInt(11));
            refer.setAvailable(ThreadLocalRandom.current().nextInt(2) == 1 ? true : false);
            refer.setMethod("sayHello");
            refer.setServiceKey("com.zzx.DemoService");
            refer.setWeight(weights[i-1]);
            refers.add(refer);
        }
    }

    private static Refer roundRobinWeight() {
        String key = refers.get(0).getServiceKey() + "." + refers.get(0).getMethod();
        int length = refers.size();
        //最大權重
        int maxWeight = 0;
        //最小權重
        int minWeight = Integer.MAX_VALUE;
        final LinkedHashMap<Refer, IntegerWrapper> referToWeightMap = new LinkedHashMap<>();
        //權重總和
        int weightSum = 0;

        //下面這個循環主要用於查找最大和最小權重,計算權重總和
        for (int i = 0; i < length; i++) {
            int weight = refers.get(i).getWeight();
            //獲取權重最大和最小值
            maxWeight = Math.max(maxWeight, weight);
            minWeight = Math.min(minWeight, weight);
            if(weight > 0) {
                referToWeightMap.put(refers.get(i), new IntegerWrapper(weight));
                //累加權重
                weightSum += weight;
            }
        }

        //獲取當前服務對應的調用序列對象 AtomicPositiveInteger,默認爲0
        AtomicPositiveInteger sequence = sequences.get(key);
        if(sequence == null) {
            sequences.putIfAbsent(key, new AtomicPositiveInteger());
            sequence = sequences.get(key);
        }

        //獲取當前的調用編號
        int currentSequence = sequence.getAndIncrement();
        //若是最小權重小於最大權重,代表服務提供者之間的權重是不相等的
        if(maxWeight > 0 && minWeight < maxWeight) {
            //使用調用編號對權重總和進行取餘操做
            int mod = currentSequence % weightSum;
            //進行maxWeight次遍歷
            for(int i = 0; i < maxWeight; i++) {
                //遍歷 invokerToWeightMap
                for (Map.Entry<Refer, IntegerWrapper> each :  referToWeightMap.entrySet()) {
                    final Refer k = each.getKey();
                    //獲取權重包裝類數據
                    final IntegerWrapper v = each.getValue();

                    //若是 mod = 0, 且權重大於0, 此時返回相應的Invoker
                    if(mod == 0 && v.getValue() > 0) {
                        return k;
                    }

                    //mod != 0,且權重大於0,此時權重和mod分別進行自減操做
                    if(v.getValue() > 0) {
                        v.decrement();
                        mod--;
                    }
                }
            }
        }

        return refers.get(currentSequence%length);
    }

    private static final class IntegerWrapper {
        private int value;

        public IntegerWrapper(int value) {
            this.value = value;
        }

        public void decrement(){
            this.value--;
        }

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }
    }

    public static void main(String[] args) {
        for (Refer refer : refers) {
            System.out.println(refer);
        }

        System.out.println("---------------------------------------------");
        for (int i = 0; i < 8; i++) {
            System.out.println("獲取按權重輪詢Refer:" + roundRobinWeight());
        }
    }
}

測試結果:apache

總結:該算法權重設置比較大,mod比較大,會致使循環次數比較多,嚴重影響性能,得到Refer對象的性能。服務器

 

方案二輪詢權重算法以下:併發

public class RoundRobinWeightLoadBalance2 {

    public static List<Refer> refers = new ArrayList<Refer>();

    private static final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<>();
    private static final ConcurrentMap<String, AtomicPositiveInteger> indexSeqs = new ConcurrentHashMap<String, AtomicPositiveInteger>();

    private static int[] weights = new int[] {5, 1, 1};
    private static String[] names = new String[] {"A", "B", "C"};

    static {
        for (int i = 1; i < 4; i++) {
            Refer refer = new Refer();
            refer.setServerName(names[i-1]);
            refer.setActiveRefererCount(ThreadLocalRandom.current().nextInt(11));
            refer.setAvailable(ThreadLocalRandom.current().nextInt(2) == 1 ? true : false);
            refer.setMethod("sayHello");
            refer.setServiceKey("com.zzx.DemoService");
            refer.setWeight(weights[i-1]);
            refers.add(refer);
        }
    }

    private static Refer roundRobinWeight() {
        String key = refers.get(0).getServiceKey() + "." + refers.get(0).getMethod();
        int length = refers.size();
        //最大權重
        int maxWeight = 0;
        //最小權重
        int minWeight = Integer.MAX_VALUE;
        final List<Refer> invokerToWeightList = new ArrayList<>();


        //下面這個循環主要用於查找最大和最小權重,計算權重總和
        for (int i = 0; i < length; i++) {
            int weight = refers.get(i).getWeight();
            //獲取權重最大和最小值
            maxWeight = Math.max(maxWeight, weight);
            minWeight = Math.min(minWeight, weight);
            if(weight > 0) {
                invokerToWeightList.add(refers.get(i));
            }
        }

        //獲取當前服務對應的調用序列對象 AtomicPositiveInteger,默認爲0
        AtomicPositiveInteger sequence = sequences.get(key);
        if(sequence == null) {
            sequences.putIfAbsent(key, new AtomicPositiveInteger());
            sequence = sequences.get(key);
        }

        // 獲取下標序列對象 AtomicPositiveInteger
        AtomicPositiveInteger indexSeq = indexSeqs.get(key);
        if (indexSeq == null) {
            // 建立 AtomicPositiveInteger,默認值爲 -1
            indexSeqs.putIfAbsent(key, new AtomicPositiveInteger(-1));
            indexSeq = indexSeqs.get(key);
        }

        if (maxWeight > 0 && minWeight < maxWeight) {
            length = invokerToWeightList.size();
            while (true) {
                //經過循環,依次獲取list的下標
                int index = indexSeq.incrementAndGet() % length;
                //得到每個請求,當前權重值
                int currentWeight = sequence.get() % maxWeight;

                // 每循環一輪(index = 0),從新計算 currentWeight
                if (index == 0) {
                    currentWeight = sequence.incrementAndGet() % maxWeight;
                }

                // 檢測 Invoker 的權重是否大於 currentWeight,大於則返回
                if (invokerToWeightList.get(index).getWeight() > currentWeight) {
                    return invokerToWeightList.get(index);
                }
            }
        }


        return refers.get(sequence.incrementAndGet() % length);
    }

    private static final class IntegerWrapper {
        private int value;

        public IntegerWrapper(int value) {
            this.value = value;
        }

        public void decrement(){
            this.value--;
        }

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }
    }

    public static void main(String[] args) {
        for (Refer refer : refers) {
            System.out.println(refer);
        }

        System.out.println("---------------------------------------------");
        for (int i = 0; i < 8; i++) {
            System.out.println("獲取按權重輪詢Refer:" + roundRobinWeight());
        }
    }
}

測試結果app

總結負載均衡

雖然該方案解決權重大時,產生性能的問題。可是從測試結果發現,會致使ServiceA在某一個時刻大量請求併發,增大服務器A的壓力。dom

 

針對方案二的問題可參考nignx,輪詢權重算法實現,使用平滑加權實現。ide

Nginx 的平滑加權輪詢負載均衡。每一個服務器對應兩個權重,分別爲 weight 和 currentWeight。其中 weight 是固定的,currentWeight 會動態調整,初始值爲0。當有新的請求進來時,遍歷服務器列表,讓它的 currentWeight 加上自身權重。遍歷完成後,找到最大的 currentWeight,並將其減去權重總和,而後返回相應的服務器便可。性能

上面描述不是很好理解,下面仍是舉例進行說明。這裏仍然使用服務器 [A, B, C] 對應權重 [5, 1, 1] 的例子說明,如今有7個請求依次進入負載均衡邏輯,選擇過程以下:

參考連接:

http://dubbo.apache.org/zh-cn/docs/source_code_guide/loadbalance.html

相關文章
相關標籤/搜索