分布式技术原理与算法解析05-如何使用负载均衡算法

在这里插入图片描述

  • Author: shasha
  • Description: 该文简单介绍微服务技术栈有哪些分别用来做什么

如何使用负载均衡算法

随机算法
  • 随机算法,就是从可用服务节点随机挑选一个节点来访问
  • 实现:随机算法通过生成随机数来实现,例如服务有10个节点,随机生成数字2,就访问2的这个节点
/*
 *  Copyright 2009-2016 Weibo, Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.weibo.api.motan.cluster.loadbalance;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

import com.weibo.api.motan.core.extension.SpiMeta;
import com.weibo.api.motan.rpc.Referer;
import com.weibo.api.motan.rpc.Request;

/**
 * 
 * random load balance.
 *
 * @author fishermen
 * @version V1.0 created at: 2013-5-21
 */
@SpiMeta(name = "random")
public class RandomLoadBalance<T> extends AbstractLoadBalance<T> {

    @Override
    protected Referer<T> doSelect(Request request) {
        List<Referer<T>> referers = getReferers();

        int idx = (int) (ThreadLocalRandom.current().nextDouble() * referers.size());
        for (int i = 0; i < referers.size(); i++) {
            Referer<T> ref = referers.get((i + idx) % referers.size());
            if (ref.isAvailable()) {
                return ref;
            }
        }
        return null;
    }

    @Override
    protected void doSelectToHolder(Request request, List<Referer<T>> refersHolder) {
        List<Referer<T>> referers = getReferers();

        int idx = (int) (ThreadLocalRandom.current().nextDouble() * referers.size());
        for (int i = 0; i < referers.size(); i++) {
            Referer<T> referer = referers.get((i + idx) % referers.size());
            if (referer.isAvailable()) {
                refersHolder.add(referer);
            }
        }
    }
}
轮询算法
  • 轮询算法,就是按照指定的顺序,把可用的服务节点挨个访问一次
  • 轮询算法通常是吧可用节点放到一个数组里面,然后按照数组编号,挨个访问。
/*
 *  Copyright 2009-2016 Weibo, Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.weibo.api.motan.cluster.loadbalance;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import com.weibo.api.motan.core.extension.SpiMeta;
import com.weibo.api.motan.rpc.Referer;
import com.weibo.api.motan.rpc.Request;
import com.weibo.api.motan.util.MathUtil;

/**
 * 
 * Round robin loadbalance.
 * 
 * @author fishermen
 * @version V1.0 created at: 2013-6-13
 */
@SpiMeta(name = "roundrobin")
public class RoundRobinLoadBalance<T> extends AbstractLoadBalance<T> {

    private AtomicInteger idx = new AtomicInteger(0);

    @Override
    protected Referer<T> doSelect(Request request) {
        List<Referer<T>> referers = getReferers();

        int index = getNextNonNegative();
        for (int i = 0; i < referers.size(); i++) {
            Referer<T> ref = referers.get((i + index) % referers.size());
            if (ref.isAvailable()) {
                return ref;
            }
            idx.incrementAndGet();
        }
        return null;
    }

    @Override
    protected void doSelectToHolder(Request request, List<Referer<T>> refersHolder) {
        List<Referer<T>> referers = getReferers();

        int index = getNextNonNegative();
        for (int i = 0, count = 0; i < referers.size() && count < MAX_REFERER_COUNT; i++) {
            Referer<T> referer = referers.get((i + index) % referers.size());
            if (referer.isAvailable()) {
                refersHolder.add(referer);
                count++;
            }
        }
    }

    // get non-negative int
    private int getNextNonNegative() {
        return MathUtil.getNonNegative(idx.incrementAndGet());
    }
}
加权轮询算法
  • 加权轮询就是给每个节点赋予一个权重,从而使每个节点被访问的概率不同,权重大的节点被访问的概率就高,权重小的节点被访问的概率就小。
  • 实现:加权轮询就是生成一个随机序列,该序列里有n个节点,n是所有节点的权重之和,在这个序列中,每个节点出现的次数就是它的权重值,例如a,b,c三个权重值分别是3,2,1,那么它生成的序列就是{a,b,c,a,a,b}
/*
 *  Copyright 2009-2016 Weibo, Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.weibo.api.motan.cluster.loadbalance;

import com.weibo.api.motan.core.extension.SpiMeta;
import com.weibo.api.motan.rpc.Referer;
import com.weibo.api.motan.rpc.Request;
import com.weibo.api.motan.util.CollectionUtil;
import com.weibo.api.motan.util.LoggerUtil;
import com.weibo.api.motan.util.MathUtil;

import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 权重可配置的负载均衡器
 *
 * @author chengya1
 */
@SpiMeta(name = "configurableWeight")
public class ConfigurableWeightLoadBalance<T> extends ActiveWeightLoadBalance<T> {

    @SuppressWarnings("rawtypes")
    private static final RefererListCacheHolder emptyHolder = new EmptyHolder();

    @SuppressWarnings("unchecked")
    private volatile RefererListCacheHolder<T> holder = emptyHolder;

    private String weightString;

    @SuppressWarnings("unchecked")
    @Override
    public void onRefresh(List<Referer<T>> referers) {
        super.onRefresh(referers);

        if (CollectionUtil.isEmpty(referers)) {
            holder = emptyHolder;
        } else if (StringUtils.isEmpty(weightString)) {
            holder = new SingleGroupHolder<T>(referers);
        } else {
            holder = new MultiGroupHolder<T>(weightString, referers);
        }
    }

    @Override
    protected Referer<T> doSelect(Request request) {
        if (holder == emptyHolder) {
            return null;
        }

        RefererListCacheHolder<T> h = this.holder;
        Referer<T> r = h.next();
        if (!r.isAvailable()) {
            int retryTimes = getReferers().size() - 1;
            for (int i = 0; i < retryTimes; i++) {
                r = h.next();
                if (r.isAvailable()) {
                    break;
                }
            }
        }
        if (r.isAvailable()) {
            return r;
        } else {
            noAvailableReferer();
            return null;
        }
    }

    @Override
    protected void doSelectToHolder(Request request, List<Referer<T>> refersHolder) {
        if (holder == emptyHolder) {
            return;
        }

        RefererListCacheHolder<T> h = this.holder;
        int i = 0, j = 0;
        while (i++ < getReferers().size()) {
            Referer<T> r = h.next();
            if (r.isAvailable()) {
                refersHolder.add(r);
                if (++j == MAX_REFERER_COUNT) {
                    return;
                }
            }
        }
        if (refersHolder.isEmpty()) {
            noAvailableReferer();
        }
    }

    private void noAvailableReferer() {
        LoggerUtil.error(this.getClass().getSimpleName() + " 当前没有可用连接, pool.size=" + getReferers().size());
    }

    @Override
    public void setWeightString(String weightString) {
        this.weightString = weightString;
    }


    /*****************************************************************************************
     * ************************************************************************************* *
     *****************************************************************************************/
    static abstract class RefererListCacheHolder<T> {
        abstract Referer<T> next();
    }

    static class EmptyHolder<T> extends RefererListCacheHolder<T> {
        @Override
        Referer<T> next() {
            return null;
        }
    }

    @SuppressWarnings("hiding")
    class SingleGroupHolder<T> extends RefererListCacheHolder<T> {

        private int size;
        private List<Referer<T>> cache;

        SingleGroupHolder(List<Referer<T>> list) {
            cache = list;
            size = list.size();
            LoggerUtil.info("ConfigurableWeightLoadBalance build new SingleGroupHolder.");
        }

        @Override
        Referer<T> next() {
            return cache.get(ThreadLocalRandom.current().nextInt(size));
        }
    }

    @SuppressWarnings("hiding")
    class MultiGroupHolder<T> extends RefererListCacheHolder<T> {

        private int randomKeySize = 0;
        private List<String> randomKeyList = new ArrayList<String>();
        private Map<String, AtomicInteger> cursors = new HashMap<String, AtomicInteger>();
        private Map<String, List<Referer<T>>> groupReferers = new HashMap<String, List<Referer<T>>>();

        MultiGroupHolder(String weights, List<Referer<T>> list) {
            LoggerUtil.info("ConfigurableWeightLoadBalance build new MultiGroupHolder. weights:" + weights);
            String[] groupsAndWeights = weights.split(",");
            int[] weightsArr = new int[groupsAndWeights.length];
            Map<String, Integer> weightsMap = new HashMap<String, Integer>(groupsAndWeights.length);
            int i = 0;
            for (String groupAndWeight : groupsAndWeights) {
                String[] gw = groupAndWeight.split(":");
                if (gw.length == 2) {
                    Integer w = Integer.valueOf(gw[1]);
                    weightsMap.put(gw[0], w);
                    groupReferers.put(gw[0], new ArrayList<Referer<T>>());
                    weightsArr[i++] = w;
                }
            }

            // 求出最大公约数,若不为1,对权重做除法
            int weightGcd = findGcd(weightsArr);
            if (weightGcd != 1) {
                for(Map.Entry<String,Integer> entry: weightsMap.entrySet()) {
                    weightsMap.put(entry.getKey(),entry.getValue()/weightGcd);
                }
            }

            for (Map.Entry<String, Integer> entry : weightsMap.entrySet()) {
                for (int j = 0; j < entry.getValue(); j++) {
                    randomKeyList.add(entry.getKey());
                }
            }
            Collections.shuffle(randomKeyList);
            randomKeySize = randomKeyList.size();

            for (String key : weightsMap.keySet()) {
                cursors.put(key, new AtomicInteger(0));
            }

            for (Referer<T> referer : list) {
                groupReferers.get(referer.getServiceUrl().getGroup()).add(referer);
            }
        }

        @Override
        Referer<T> next() {
            String group = randomKeyList.get(ThreadLocalRandom.current().nextInt(randomKeySize));
            AtomicInteger ai = cursors.get(group);
            List<Referer<T>> referers = groupReferers.get(group);
            return referers.get(MathUtil.getNonNegative(ai.getAndIncrement()) % referers.size());
        }

        // 求最大公约数
        private int findGcd(int n, int m) {
            return (n == 0 || m == 0) ? n + m : findGcd(m, n % m);
        }

        // 求最大公约数
        private int findGcd(int[] arr) {
            int i = 0;
            for (; i < arr.length - 1; i++) {
                arr[i + 1] = findGcd(arr[i], arr[i + 1]);
            }
            return findGcd(arr[i], arr[i - 1]);
        }
    }

}

最少活跃连接算法
  • 最少活跃度算法,每一次访问都选择连接数最少的节点。连接数大的可以认为是处理请求慢,连接数小的可以认为是处理请求魁岸。
/*
 *  Copyright 2009-2016 Weibo, Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.weibo.api.motan.cluster.loadbalance;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

import com.weibo.api.motan.core.extension.SpiMeta;
import com.weibo.api.motan.rpc.Referer;
import com.weibo.api.motan.rpc.Request;

/**
 * "低并发优化" 负载均衡
 * 
 * <pre>
 * 		1) 低并发度优先: referer的某时刻的call数越小优先级越高 
 * 
 * 		2) 低并发referer获取策略:
 * 				由于Referer List可能很多,比如上百台,如果每次都要从这上百个Referer或者最低并发的几个,性能有些损耗,
 * 				因此 random.nextInt(list.size()) 获取一个起始的index,然后获取最多不超过MAX_REFERER_COUNT的
 * 				状态是isAvailable的referer进行判断activeCount.
 * </pre>
 * 
 * @author maijunsheng
 * @version 创建时间:2013-6-14
 * 
 */
@SpiMeta(name = "activeWeight")
public class ActiveWeightLoadBalance<T> extends AbstractLoadBalance<T> {

    @Override
    protected Referer<T> doSelect(Request request) {
        List<Referer<T>> referers = getReferers();

        int refererSize = referers.size();
        int startIndex = ThreadLocalRandom.current().nextInt(refererSize);
        int currentCursor = 0;
        int currentAvailableCursor = 0;

        Referer<T> referer = null;

        while (currentAvailableCursor < MAX_REFERER_COUNT && currentCursor < refererSize) {
            Referer<T> temp = referers.get((startIndex + currentCursor) % refererSize);
            currentCursor++;

            if (!temp.isAvailable()) {
                continue;
            }

            currentAvailableCursor++;

            if (referer == null) {
                referer = temp;
            } else {
                if (compare(referer, temp) > 0) {
                    referer = temp;
                }
            }
        }

        return referer;
    }

    @Override
    protected void doSelectToHolder(Request request, List<Referer<T>> refersHolder) {
        List<Referer<T>> referers = getReferers();

        int refererSize = referers.size();
        int startIndex = ThreadLocalRandom.current().nextInt(refererSize);
        int currentCursor = 0;
        int currentAvailableCursor = 0;

        while (currentAvailableCursor < MAX_REFERER_COUNT && currentCursor < refererSize) {
            Referer<T> temp = referers.get((startIndex + currentCursor) % refererSize);
            currentCursor++;

            if (!temp.isAvailable()) {
                continue;
            }

            currentAvailableCursor++;

            refersHolder.add(temp);
        }

        Collections.sort(refersHolder, new LowActivePriorityComparator<T>());
    }

    private int compare(Referer<T> referer1, Referer<T> referer2) {
        return referer1.activeRefererCount() - referer2.activeRefererCount();
    }

    static class LowActivePriorityComparator<T> implements Comparator<Referer<T>> {
        @Override
        public int compare(Referer<T> referer1, Referer<T> referer2) {
            return referer1.activeRefererCount() - referer2.activeRefererCount();
        }
    }

}

一致hash算法
  • 一致性hash算法,是通过某个hash函数,把同一个来源的请求都映射到同一个节点上。
/*
 *  Copyright 2009-2016 Weibo, Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.weibo.api.motan.cluster.loadbalance;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import com.weibo.api.motan.common.MotanConstants;
import com.weibo.api.motan.core.extension.SpiMeta;
import com.weibo.api.motan.rpc.Referer;
import com.weibo.api.motan.rpc.Request;
import com.weibo.api.motan.util.MathUtil;

/**
 * 
 * Use consistent hash to choose referer
 *
 * @author fishermen
 * @version V1.0 created at: 2013-5-21
 */
@SpiMeta(name = "consistent")
public class ConsistentHashLoadBalance<T> extends AbstractLoadBalance<T> {

    private List<Referer<T>> consistentHashReferers;

    @Override
    public void onRefresh(List<Referer<T>> referers) {
        super.onRefresh(referers);

        List<Referer<T>> copyReferers = new ArrayList<Referer<T>>(referers);
        List<Referer<T>> tempRefers = new ArrayList<Referer<T>>();
        for (int i = 0; i < MotanConstants.DEFAULT_CONSISTENT_HASH_BASE_LOOP; i++) {
            Collections.shuffle(copyReferers);
            for (Referer<T> ref : copyReferers) {
                tempRefers.add(ref);
            }
        }

        consistentHashReferers = tempRefers;
    }

    @Override
    protected Referer<T> doSelect(Request request) {

        int hash = getHash(request);
        Referer<T> ref;
        for (int i = 0; i < getReferers().size(); i++) {
            ref = consistentHashReferers.get((hash + i) % consistentHashReferers.size());
            if (ref.isAvailable()) {
                return ref;
            }
        }
        return null;
    }

    @Override
    protected void doSelectToHolder(Request request, List<Referer<T>> refersHolder) {
        List<Referer<T>> referers = getReferers();

        int hash = getHash(request);
        for (int i = 0; i < referers.size(); i++) {
            Referer<T> ref = consistentHashReferers.get((hash + i) % consistentHashReferers.size());
            if (ref.isAvailable()) {
                refersHolder.add(ref);
            }
        }
    }

    private int getHash(Request request) {
        int hashcode;
        if (request.getArguments() == null || request.getArguments().length == 0) {
            hashcode = request.hashCode();
        } else {
            hashcode = Arrays.hashCode(request.getArguments());
        }
        return MathUtil.getNonNegative(hashcode);
    }


}
`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值