負(fù)載均衡算法--Dubbo、Nginx

概述

比較經(jīng)典的5種負(fù)載均衡算法:隨機(jī)法猎莲、輪詢法绍弟、最少連接數(shù)法、最快響應(yīng)法益眉、Hash化散列法(包括IP-Hash和參數(shù)值Hash一致性算法)晌柬,另外還可以整合權(quán)重(配置權(quán)重值和JVM預(yù)熱啟動(dòng)加權(quán))

  1. 隨機(jī)法:實(shí)現(xiàn)比較簡單,也不需要記住狀態(tài)位郭脂,每次隨機(jī)選舉驰唬,實(shí)現(xiàn)負(fù)載均衡的同時(shí)又避免了在選取節(jié)點(diǎn)時(shí)候的復(fù)雜運(yùn)算
  2. 輪詢法:實(shí)現(xiàn)更公平的負(fù)載均攤鳄袍,但是是基于所有訪問的服務(wù)器處理響應(yīng)時(shí)間差不多的業(yè)務(wù)場景
  3. 最少連接數(shù)法:實(shí)現(xiàn)了更貼合實(shí)際場景的負(fù)載均攤写半,真正實(shí)現(xiàn)了根據(jù)服務(wù)器的實(shí)際處理能力來分?jǐn)傉?qǐng)求畅卓,避免了慢堆積
  4. 通過統(tǒng)計(jì)每個(gè)Server的平均響應(yīng)時(shí)間,然后選取最快的server莹弊,可以實(shí)現(xiàn)動(dòng)態(tài)的調(diào)整負(fù)載的均攤涤久。
  5. Hash化散列法:IP哈希可以解決集群的Session共享問題忍弛,Hash一致性解決的是在非常復(fù)雜的集群模式下响迂,頻繁發(fā)生節(jié)點(diǎn)的新增和刪除的時(shí)候,如何實(shí)現(xiàn)影響最小的請(qǐng)求均攤细疚。
  6. 權(quán)重值的引入蔗彤,非常有意義的一個(gè)干預(yù)參數(shù),因?yàn)閷?shí)際的業(yè)務(wù)場景,每臺(tái)服務(wù)器的物理環(huán)境所導(dǎo)致的服務(wù)性能各不相同然遏∑锻荆可以和隨機(jī)法、輪訓(xùn)法待侵、最少連接數(shù)法結(jié)合起來用丢早,在和輪詢法結(jié)合起來用時(shí),又有平滑的負(fù)載均攤和不是很平滑的負(fù)載均攤秧倾。

總體來看怨酝,Dubbo提供的負(fù)載均衡的方法最多,但是負(fù)載的實(shí)現(xiàn)問題也多中狂,性能也有待優(yōu)化凫碌。Nginx次之扑毡,但是功能也很豐富胃榕、性能都較好。

Dubbo負(fù)載均衡算法 2.6.2版本

Dubbo提供了功能豐富(bug也多)的4種負(fù)載均衡算法瞄摊,解決Consumer如何從Provider集群間選擇哪個(gè)Provider提供服務(wù)的問題勋又。
另外Dubbo在負(fù)載均衡時(shí)引入了自定義權(quán)重配置、JVM預(yù)熱時(shí)間加權(quán)的規(guī)則進(jìn)來换帜。

四種算法:

  1. Random LoadBalance:按照權(quán)重隨機(jī)分配Provider楔壤,比如隨機(jī)且權(quán)重Node1:Node2= 2:1,那么運(yùn)行30次惯驼,大約有20次在Node1上蹲嚣,10次在Node2上。

  2. RoundRobin LoadBalance:按照權(quán)重輪詢分配祟牲。比如權(quán)重Node1:Node2= 20:10隙畜,那么運(yùn)行30次:前20次里面輪詢Node1和Node2大家各10次,第20次到30次说贝,全部選擇Node1议惰。因?yàn)镈ubbo默認(rèn)是不會(huì)做公約數(shù)的處理,只有完成一個(gè)完整的20+10次運(yùn)算乡恕,才能保證負(fù)載均衡的權(quán)重比例準(zhǔn)確言询,如果Consumer只調(diào)用了20次,那么這里配置的權(quán)重的結(jié)果就是1:1了傲宜,該算法很不平滑运杭。

  3. LeastActive LoadBalance:節(jié)點(diǎn)處理越快分配更多,避免慢節(jié)點(diǎn)堆積函卒,每次篩選Provider的時(shí)候辆憔,都只取Active值最小的節(jié)點(diǎn),如果最小Active值的節(jié)點(diǎn)有多個(gè),則按照權(quán)重隨機(jī)選取躁愿。Provider每獲取到一個(gè)任務(wù)Active值++叛本,每結(jié)束一個(gè)任務(wù)Active值--

  4. ConsistentHash LoadBalance:唯一忽略權(quán)重配置和JVM預(yù)熱的算法。先把所有Provider都分配160個(gè)虛擬節(jié)點(diǎn)彤钟,通過Hash算法来候,全部分散到Hash圓上。每次Consumer調(diào)用時(shí)逸雹,會(huì)根據(jù)參數(shù)值做Hash換算营搅,最后映射到Hash圓上,找到鄰近的虛擬節(jié)點(diǎn)梆砸,最終獲取到提供服務(wù)的Provider转质。但是Dubbo在實(shí)現(xiàn)的時(shí)候違背了Hash一致性的原則,每次Porvider發(fā)生改變的時(shí)候(新增或者剔除)帖世,都會(huì)重新創(chuàng)建一個(gè)Hash圓休蟹,而不是在之前的Hash圓上新增或者剔除不合格的Porvider

AbstractLoadBalance抽象類

這個(gè)類提供了計(jì)算權(quán)重的方法,該方法里面會(huì)根據(jù)JVM啟動(dòng)時(shí)間做加權(quán)日矫,并且直接處理了只有1個(gè)Provider或者沒有Provider的情況赂弓,通過doSelect抽象方法,讓4種負(fù)載均衡實(shí)現(xiàn)類去實(shí)現(xiàn)各自的規(guī)則哪轿。

  1. 獲取服務(wù)節(jié)點(diǎn)的時(shí)候盈魁,首先調(diào)用的是AbstractLoadBalance的select()方法,該方法對(duì)一些只有1個(gè)Provider或者沒有Provider做了處理窃诉,如果可用Porvider不止1個(gè)杨耙,配置的算法才有意義
 public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        if (invokers == null || invokers.size() == 0)
            return null;
        if (invokers.size() == 1)
            return invokers.get(0);
        return doSelect(invokers, url, invocation);
    }
  1. 因?yàn)閖vm重啟后有一段預(yù)熱過程,要運(yùn)行一段時(shí)間飘痛,它的性能才能達(dá)到最佳珊膜,所以Dubbo在做負(fù)載均衡計(jì)算Provider的權(quán)重時(shí),引入了warmupTime的加權(quán)的算法敦冬。
    在AbstractLoadBalance里面getWeight方法里面:weight= weight * (啟動(dòng)時(shí)間/逾期預(yù)熱時(shí)間)辅搬,warmup默認(rèn)10分鐘,Provider的權(quán)重會(huì)隨著啟動(dòng)時(shí)間的增長脖旱,取的權(quán)重值增加堪遂,到了10分鐘后,才是真正的配置的權(quán)重值
    static int calculateWarmupWeight(int uptime, int warmup, int weight) {
        int ww = (int) ( (float) uptime / ( (float) warmup / (float) weight ) );
        return ww < 1 ? 1 : (ww > weight ? weight : ww);
    }
  1. 備注:2.5.3版本代碼里面有bug萌庆,在求當(dāng)前Provider的運(yùn)行時(shí)間參數(shù)的時(shí)候溶褪,實(shí)際上取的是當(dāng)前Consumer的jvm啟動(dòng)時(shí)間,不過后來修復(fù)了
  正確的取值參數(shù)為:REMOTE_TIMESTAMP_KEY践险。2.5.3版本的參數(shù)為 TIMESTAMP_KEY
  long timestamp = invoker.getUrl().getParameter(Constants.REMOTE_TIMESTAMP_KEY, 0L);

RandomLoadBalance源碼分析:

  • 先求總權(quán)重猿妈,如果權(quán)重有不相等的吹菱,就根據(jù)總權(quán)重為上限生成隨機(jī)值,然后看該隨機(jī)值落在哪個(gè)Node上
  • 如果權(quán)重未配置或者所有節(jié)點(diǎn)權(quán)重相同彭则,就按照節(jié)點(diǎn)數(shù)做隨機(jī)取值
public class RandomLoadBalance extends AbstractLoadBalance {
    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        int length = invokers.size(); // 總個(gè)數(shù)
        int totalWeight = 0; // 總權(quán)重
        boolean sameWeight = true; // 權(quán)重是否都一樣
        for (int i = 0; i < length; i++) {
            int weight = getWeight(invokers.get(i), invocation);
            totalWeight += weight; // 累計(jì)總權(quán)重
            if (sameWeight && i > 0 && weight != getWeight(invokers.get(i - 1), invocation)) {
                sameWeight = false; // 計(jì)算所有權(quán)重是否一樣
            }
        }
        if (totalWeight > 0 && ! sameWeight) {
            int offset = random.nextInt(totalWeight);
            for (int i = 0; i < length; i++) {
                offset -= getWeight(invokers.get(i), invocation);
                if (offset < 0) {
                    return invokers.get(i);
                }
            }
        }
        return invokers.get(random.nextInt(length));
    }
}

RoundRobinLoadBalance源碼分析

核心思想

核心思想是先均攤鳍刷,小權(quán)重的節(jié)點(diǎn)權(quán)重使用完后被淘汰,大權(quán)重節(jié)點(diǎn)之間均攤俯抖,逐個(gè)淘汰输瓜,最后是最大的一個(gè)節(jié)點(diǎn)獨(dú)占。
比如Node1芬萍、Node2尤揣、Node3 :1:5:10的權(quán)重,那么16次調(diào)用的結(jié)果就是
(Node1柬祠,Node2北戏,Node3)
(Node2,Node3漫蛔,Node2嗜愈,Node3,Node2惩猫,Node3芝硬,Node2蚜点,Node3)
(node3轧房,node3,node3绍绘,node3奶镶,node3)

缺點(diǎn)1:不夠平滑,如果只調(diào)用了10次陪拘,那么權(quán)重類似于1:5:4
缺點(diǎn)2:沒有類似公約數(shù)的處理厂镇,節(jié)點(diǎn)1:節(jié)點(diǎn)2設(shè)置1:10和 100萬:1000萬權(quán)重的耗時(shí)相差極大
缺點(diǎn)3:大權(quán)重值時(shí)循環(huán)次數(shù)太多,第1000萬次的調(diào)用的時(shí)候左刽,會(huì)循環(huán)1000*2次才能判斷出哪個(gè)節(jié)點(diǎn)來處理請(qǐng)求捺信,節(jié)點(diǎn)數(shù)越多,權(quán)重值設(shè)置越大越嚴(yán)重欠痴。

改進(jìn)方案:

  1. 引入公約數(shù)迄靠,或者按照1-100進(jìn)行折算
  2. 借鑒Nginx的平滑的權(quán)重加權(quán)輪詢的算法

代碼分析

  1. 通過一個(gè)全局的sequences根據(jù)key來存儲(chǔ)調(diào)用的值。每個(gè)方法對(duì)應(yīng)一個(gè)key喇辽,value來標(biāo)記該方法是第幾次被調(diào)用掌挚。
  2. 通過預(yù)熱加權(quán)后的權(quán)重,計(jì)算出所有Provider的最大權(quán)重菩咨、最小權(quán)重吠式、累計(jì)權(quán)重
  3. 2層for循環(huán)自減陡厘,第一層for循環(huán)的上線時(shí)maxWeight,第二層循環(huán)的次數(shù)是list<invokers>.size
 private final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();
 protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
        int length = invokers.size(); // Number of invokers
        int maxWeight = 0; // The maximum weight
        int minWeight = Integer.MAX_VALUE; // The minimum weight
        final LinkedHashMap<Invoker<T>, IntegerWrapper> invokerToWeightMap = new LinkedHashMap<Invoker<T>, IntegerWrapper>();
        int weightSum = 0;
        for (int i = 0; i < length; i++) {
            int weight = getWeight(invokers.get(i), invocation);
            maxWeight = Math.max(maxWeight, weight); // Choose the maximum weight
            minWeight = Math.min(minWeight, weight); // Choose the minimum weight
            if (weight > 0) {
                invokerToWeightMap.put(invokers.get(i), new IntegerWrapper(weight));
                weightSum += weight;
            }
        }
        AtomicPositiveInteger sequence = sequences.get(key);
        if (sequence == null) {
            sequences.putIfAbsent(key, new AtomicPositiveInteger());
            sequence = sequences.get(key);
        }

        //計(jì)算當(dāng)前方法是第幾次發(fā)起的調(diào)用
        int currentSequence = sequence.getAndIncrement();

        //如果Providers之間的權(quán)重不相同特占,會(huì)按照權(quán)重來進(jìn)行輪詢Provider
        if (maxWeight > 0 && minWeight < maxWeight) {
            //關(guān)鍵的兩層循環(huán)糙置,調(diào)用次數(shù)會(huì)按照weightSum的余數(shù)來循環(huán)計(jì)算
            int mod = currentSequence % weightSum;
            //這里的上限是maxWeight,因?yàn)?里面會(huì)有invokerList.size()次的判斷是目,maxWeight*size >sumWeight
            for (int i = 0; i < maxWeight; i++) {
                //不管該invoker的權(quán)重是否自減完了罢低,仍然要取值判斷
                for (Map.Entry<Invoker<T>, IntegerWrapper> each : invokerToWeightMap.entrySet()) {
                    final Invoker<T> k = each.getKey();
                    final IntegerWrapper v = each.getValue();
                    if (mod == 0 && v.getValue() > 0) {
                        return k;
                    }
                    //只有當(dāng)value<0的時(shí)候,該節(jié)點(diǎn)已經(jīng)被淘汰胖笛,沒有資格自減mod
                    if (v.getValue() > 0) {
                        v.decrement();
                        mod--;
                    }
                }
            }
        }
        // Round robin
        return invokers.get(currentSequence % length);
    }

LeastActiveLoadBalance

  1. 配置:需要設(shè)置Consumer的filter="activelimit"
<dubbo:reference id="demoService" interface="..."  loadbalance="leastactive"  filter="activelimit"/>
  1. 算法邏輯
    每個(gè)Provider對(duì)象里面有active值网持,抽選節(jié)點(diǎn)的時(shí)候優(yōu)先判斷Active是否是最小的,再根據(jù)權(quán)重值最隨機(jī)抽選節(jié)點(diǎn)长踊,這樣避免讓調(diào)用堆積在速度相應(yīng)慢的節(jié)點(diǎn)上面
  • 如果最小active值的Provider只有1個(gè)功舀,那么就調(diào)用這個(gè)Provider
  • 如果最小active值的Provider有多個(gè),則用一個(gè)數(shù)組存起來身弊,在這個(gè)數(shù)組中的Provider按照權(quán)重值做隨機(jī)抽選
  • 如果大家的Active值都一樣辟汰,且權(quán)重也都一樣,就隨機(jī)抽選
 protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
...
    //遍歷所有節(jié)點(diǎn)阱佛,獲取幾個(gè)指標(biāo)
    //用int[] leastIndexs 保存所有活躍值都最小且相同的Providers的下標(biāo)
    //totalWeight 只獲取該數(shù)組的Providers的totalWeight,不是最小的Provider不統(tǒng)計(jì)
    // boolean sameWeight 判斷該數(shù)組中的Providers的權(quán)重是否都相同
    for (int i = 0; i < length; i++) {
 ...
    if (leastActive == -1 || active < leastActive) { // 發(fā)現(xiàn)更小的活躍數(shù)帖汞,重新開始
            leastActive = active; // 記錄最小活躍數(shù)
            leastCount = 1; // 重新統(tǒng)計(jì)相同最小活躍數(shù)的個(gè)數(shù)
            leastIndexs[0] = i; // 重新記錄最小活躍數(shù)下標(biāo)
            totalWeight = weight; // 重新累計(jì)總權(quán)重
            firstWeight = weight; // 記錄第一個(gè)權(quán)重
            sameWeight = true; // 還原權(quán)重相同標(biāo)識(shí)
        } else if (active == leastActive) { // 累計(jì)相同最小的活躍數(shù)
            leastIndexs[leastCount ++] = i; // 累計(jì)相同最小活躍數(shù)下標(biāo)
            totalWeight += weight; // 累計(jì)總權(quán)重
            // 判斷所有權(quán)重是否一樣
            if (sameWeight && i > 0 && weight != firstWeight) {
                sameWeight = false;
            }
        }
    }
...
    //如果最小活躍值數(shù)組不止一個(gè)且大家權(quán)重不相同
    //然后按照權(quán)重做隨機(jī)選取
    if (! sameWeight && totalWeight > 0) {
        // 如果權(quán)重不相同且權(quán)重大于0則按總權(quán)重?cái)?shù)隨機(jī)
        int offsetWeight = random.nextInt(totalWeight);
        // 并確定隨機(jī)值落在哪個(gè)片斷上
        for (int i = 0; i < leastCount; i++) {
            int leastIndex = leastIndexs[i];
            offsetWeight -= getWeight(invokers.get(leastIndex), invocation);
            if (offsetWeight <= 0)
                return invokers.get(leastIndex);
        }
    }
    // 權(quán)重相同或權(quán)重為0則均等隨機(jī)
    return invokers.get(leastIndexs[random.nextInt(leastCount)]);
}
  1. 活躍數(shù)統(tǒng)計(jì)ActiveLimitFilter
    配置了Filter時(shí),在開始調(diào)用方法時(shí)會(huì)beginCount凑术,active++翩蘸,方法調(diào)用結(jié)束時(shí)會(huì)active--
 public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        RpcStatus count = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());
        int active = count.getActive();
        RpcStatus.beginCount(url, methodName);
        Result result = invoker.invoke(invocation);
        RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, true);
        return result;
}

    private static void beginCount(RpcStatus status) {
        status.active.incrementAndGet();
    }

ConsistentHashLoadBalance

  1. 算法分析:
  • 根據(jù)設(shè)置的參數(shù)和參數(shù)值的拼接,來通過Hash一致性算法淮逊,獲取在圓上對(duì)應(yīng)的Provider節(jié)點(diǎn)催首,默認(rèn)配置160個(gè)虛擬節(jié)點(diǎn),只取第一個(gè)參數(shù)的參數(shù)值進(jìn)行散列
  • 實(shí)現(xiàn)的效果是:類似優(yōu)化粒度的隨機(jī)取值泄鹏,而且隨機(jī)的好壞和散列的算法關(guān)聯(lián)度很高郎任。目前的API并沒有支持動(dòng)態(tài)的擴(kuò)容和縮容,只是簡單的初始化和選取節(jié)點(diǎn)备籽。
  • 該算法不參考權(quán)重值
  1. 配置
    <dubbo:reference  id="demoService"  interface="com.mor.server.dubbo.service.DemoServer">
        <dubbo:method name="sayHello" loadbalance="consistenthash"/>
    </dubbo:reference>
  1. 源碼分析
    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
        ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key);
        return selector.select(invocation);
    }
    

    ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {
        this.virtualInvokers = new TreeMap<Long, Invoker<T>>();
        ...
        this.replicaNumber = url.getMethodParameter(methodName, "hash.nodes", 160);
        String[] index = Constants.COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, "hash.arguments", "0"));
        argumentIndex = new int[index.length];
        //構(gòu)建一個(gè)160份鏡像的散列的invoker
        for (Invoker<T> invoker : invokers) {
            String address = invoker.getUrl().getAddress();
            for (int i = 0; i < replicaNumber / 4; i++) {
                byte[] digest = md5(address + i);
                for (int h = 0; h < 4; h++) {
                    long m = hash(digest, h);
                    virtualInvokers.put(m, invoker);
                }
            }
        }

哈希一致性算法

假如有2000張圖片舶治,圖片名/圖片路徑 組成,有4臺(tái)機(jī)器來存儲(chǔ)车猬,會(huì)進(jìn)行圖片進(jìn)行CRUD操作霉猛。

  1. 如果采取隨機(jī)法存儲(chǔ),那么每次get一張圖片诈唬,最差需要遍歷所有數(shù)據(jù)才能獲取到對(duì)應(yīng)的圖片
  2. 哈希取余法韩脏,根據(jù)圖片名稱 散列成一個(gè)hashcode值,然后和4取余數(shù)铸磅,這樣就可以大致均攤到4臺(tái)服務(wù)器赡矢,而且每次get的時(shí)候杭朱,都只需要查找1個(gè)節(jié)點(diǎn)即可
  • 哈希取余法的缺點(diǎn):伸縮性不好,如果新增加一臺(tái)機(jī)器吹散,那么get圖片就會(huì)出錯(cuò)弧械,需要2000萬條數(shù)據(jù),全部重新排序空民,分配到5臺(tái)節(jié)點(diǎn)刃唐。如果考慮縮減一個(gè)節(jié)點(diǎn),也需要全部重新2000萬數(shù)據(jù)重排列
  1. 哈希一致性算法:把各個(gè)節(jié)點(diǎn)按照serverId進(jìn)行散列界轩,讓其盡可能的均勻的散布在一個(gè)最大值為 2的32次方的圓上画饥,這樣,這個(gè)圓上就有4個(gè)server節(jié)點(diǎn)浊猾,存儲(chǔ)圖片的時(shí)候抖甘,需要按照相同的算法,然后葫慎,分配到比自己值大的最近的一個(gè)節(jié)點(diǎn)衔彻。
  • 哈希一致性的優(yōu)點(diǎn):新增和縮容的時(shí)候,只會(huì)影響到下一個(gè)節(jié)點(diǎn)偷办,而不是全部節(jié)點(diǎn)要調(diào)整
  • 新增一個(gè)節(jié)點(diǎn)的時(shí)候艰额,比如之前節(jié)點(diǎn)在圓上的散落順序是順序散落,4臺(tái)server求出來的hash值分別是 1椒涯、100柄沮、200、300逐工,新增一臺(tái)機(jī)器的E節(jié)點(diǎn)hash值是250铡溪,那么只需要把落在它的上一個(gè)節(jié)點(diǎn),也就是D節(jié)點(diǎn)的所有數(shù)據(jù)(數(shù)據(jù)的key的區(qū)間是201-300)重新進(jìn)行排列泪喊,然后該圓就仍然有效,不會(huì)影響到 A髓涯、B袒啼、C節(jié)點(diǎn)的數(shù)據(jù)。


    image
  • 如果要縮容纬纪,去除掉D節(jié)點(diǎn)蚓再,只需要取出300節(jié)點(diǎn)的所有數(shù)據(jù),全部分配給A節(jié)點(diǎn)即可包各。
  1. 哈希一致性算法引入虛擬節(jié)點(diǎn):可以降低分散的不均勻性摘仅,但是會(huì)提升容量調(diào)整時(shí)的復(fù)雜性。
  • 加入虛擬數(shù)為2问畅,之前的排列是 A B C D娃属,現(xiàn)在是 A1 B1 A2 C1 C2 D1 B2 D2六荒,如果新增或者刪除A節(jié)點(diǎn),那么也只會(huì)影響到 *虛擬數(shù)的節(jié)點(diǎn)矾端,也就是 B1 和C1節(jié)點(diǎn)掏击,而不會(huì)影響其他 B2 C2 D1 D2節(jié)點(diǎn)
  • 虛擬數(shù)越大,容量變化時(shí)需要調(diào)整的數(shù)據(jù)就越多秩铆,但是虛擬數(shù)越大砚亭,數(shù)據(jù)分布的就越均勻

Nginx的負(fù)載均衡算法

結(jié)合第三方插件,可以實(shí)現(xiàn)高可用殴玛,剔除掉出問題的節(jié)點(diǎn)

Nginx目前有5種負(fù)載均衡配置:

  1. round_robin捅膘,加權(quán)輪詢,是默認(rèn)的HTTP負(fù)載均衡算法滚粟,適用于知道機(jī)器的性能篓跛,且默認(rèn)所有的請(qǐng)求對(duì)于服務(wù)器而言,處理的時(shí)間相差不大坦刀。比如我Server1 比Server2的配置要高一倍愧沟,我設(shè)置為2:1的權(quán)重,可以實(shí)現(xiàn)比較科學(xué)的負(fù)載鲤遥。算法實(shí)現(xiàn)上沐寺,簡單的輪詢很簡單,給每個(gè)Server依次編號(hào)盖奈,然后只要記錄一個(gè)調(diào)用index混坞,既可以實(shí)現(xiàn)輪詢。

  2. ip_hash钢坦,IP哈希究孕,可保持會(huì)話

  3. least_conn; 避免了慢堆積,會(huì)取連接數(shù)最小的server提供服務(wù)爹凹,可以避免有些請(qǐng)求耗時(shí)長厨诸,有些耗時(shí)端的情況。根據(jù)實(shí)際的連接數(shù)選擇服務(wù)器禾酱。

  4. fair微酬,需要插件擴(kuò)展該功能,根據(jù)后端服務(wù)器的響應(yīng)時(shí)間來分配請(qǐng)求颤陶,響應(yīng)時(shí)間短的優(yōu)先分配颗管,避免慢堆積。

  5. 權(quán)重配置:而且采用的是平滑的負(fù)載均衡算法滓走,比如node1:node2:node3=1:2:5 --> node3垦江,node3,node2搅方,node3比吭,node1绽族,node3,node2梗逮,node3

平滑的輪詢負(fù)載均衡算法(Smooth Weighted Round Robin)

例如server-a:server-b:server-c=4:2:1项秉。選取7次的話,選取的結(jié)果 server-a,server-b,server-a,server-c,server-a,server-b,server-a慷彤。
每次都篩選當(dāng)前權(quán)重值最大的節(jié)點(diǎn)娄蔼,然后對(duì)該節(jié)點(diǎn)權(quán)重值-totalWeight,然后所有的節(jié)點(diǎn)都grow一下底哗,都用當(dāng)前權(quán)重+init權(quán)重
初始化的時(shí)候大家的權(quán)重(4,2,1)岁诉,Server-a的權(quán)重最大,選他干活跋选,干完之后涕癣,a節(jié)點(diǎn)的權(quán)重-最大權(quán)重,a的當(dāng)前權(quán)重為-3前标,然后所有節(jié)點(diǎn)的權(quán)重坠韩,都按照自己的初始權(quán)重自增一次(-3+4,2+2,1+1),也就是(1,4,2)炼列,開始第二輪選取


image

java代碼實(shí)現(xiàn)

class Server{
    int initWeigth;
    int printCount=0;
    int weigth;
    String name;
}

public  List<Server> init(){
        Server server1=new Server(4,4,"server-a");
        totalWeight+=4;
        Server server2=new Server(2,2,"server-b");
        totalWeight+=2;
        Server server3=new Server(1,1,"server-c");
        totalWeight+=1;
        List<Server> list=new ArrayList();
        list.add(server1);
        list.add(server2);
        list.add(server3);
        return list;
    }

    public Server chooseServer(List<Server> list){
        Server choosenServer=list.get(0);
        for(int i=1;i<list.size();i++){
            Server server=list.get(i);
            if(choosenServer.getWeigth()<server.getWeigth()){
                choosenServer=server;
            }
        }
        choosenServer.setWeigth(choosenServer.getWeigth()-totalWeight);
        return choosenServer;
    }

    public void grow(List<Server> list){
        for(int i=0;i<list.size();i++){
            Server server=list.get(i);
            server.setWeigth(server.getWeigth()+server.getInitWeigth());
        }
    }

    public static void main(String[] args) {
        Test smooth=new Test();
        List<Server> list=smooth.init();
        
        for(int i=0;i<7;i++){
            Server server= smooth.chooseServer(list);
            System.out.println("server-"+server.getName()+"  is working");
            server.setPrintCount(server.getPrintCount()+1);
            smooth.grow(list);
        }

        System.out.println("--------------------");
        for(int i=0;i<list.size();i++){
            Server server=list.get(i);
            System.out.println(server.getName()+"  initWeight-"+server.getInitWeigth()+" totalPrint-"+server.getPrintCount()+"times");
        }
    }

參考資料

https://blog.csdn.net/revivedsun/article/category/6435629

github中文官網(wǎng)
https://dubbo.gitbooks.io/dubbo-user-book/content/preface/background.html

哈希一致性算法
https://www.cnblogs.com/lpfuture/p/5796398.html
https://blog.csdn.net/bntX2jSQfEHy7/article/details/79549368

如果你覺得對(duì)你有幫助的話只搁,就給我點(diǎn)贊吧!

或者留言夸夸我也行俭尖,讓我知道寫的這些很有意義氢惋!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市稽犁,隨后出現(xiàn)的幾起案子焰望,更是在濱河造成了極大的恐慌,老刑警劉巖已亥,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件熊赖,死亡現(xiàn)場離奇詭異,居然都是意外死亡陷猫,警方通過查閱死者的電腦和手機(jī)秫舌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绣檬,“玉大人,你說我怎么就攤上這事嫂粟〗课矗” “怎么了?”我有些...
    開封第一講書人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵星虹,是天一觀的道長零抬。 經(jīng)常有香客問我镊讼,道長,這世上最難降的妖魔是什么平夜? 我笑而不...
    開封第一講書人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任蝶棋,我火速辦了婚禮,結(jié)果婚禮上忽妒,老公的妹妹穿的比我還像新娘玩裙。我一直安慰自己,他們只是感情好段直,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開白布吃溅。 她就那樣靜靜地躺著,像睡著了一般鸯檬。 火紅的嫁衣襯著肌膚如雪决侈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,736評(píng)論 1 312
  • 那天喧务,我揣著相機(jī)與錄音赖歌,去河邊找鬼。 笑死功茴,一個(gè)胖子當(dāng)著我的面吹牛庐冯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播痊土,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼肄扎,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了赁酝?” 一聲冷哼從身側(cè)響起犯祠,我...
    開封第一講書人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎酌呆,沒想到半個(gè)月后衡载,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡隙袁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年痰娱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菩收。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡梨睁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出娜饵,到底是詐尸還是另有隱情坡贺,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站遍坟,受9級(jí)特大地震影響拳亿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜愿伴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一肺魁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧隔节,春花似錦鹅经、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至刽虹,卻和暖如春酗捌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背涌哲。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來泰國打工胖缤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人阀圾。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓哪廓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親初烘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子涡真,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容