<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          長文圖解七種負(fù)載均衡策略

          共 3233字,需瀏覽 7分鐘

           ·

          2022-02-20 14:54


          JAVA前線?


          歡迎大家關(guān)注公眾號「JAVA前線」查看更多精彩分享,主要內(nèi)容包括源碼分析、實際應(yīng)用、架構(gòu)思維、職場分享、產(chǎn)品思考等等,同時也非常歡迎大家加我微信「java_front」一起交流學(xué)習(xí)



          1 三組概念

          負(fù)載均衡、集群容錯、服務(wù)降級這三個概念在DUBBO中非常重要,同理其它分布式框架也都有相同或者相近之概念。

          從調(diào)用順序角度分析,調(diào)用順序依次是負(fù)載均衡、集群容錯、服務(wù)降級。從解決問題角度分析,負(fù)載均衡解決了「選哪一個」問題,集群容錯解決了「換哪一個」問題,服務(wù)降級解決了「全錯怎么辦」問題。

          假設(shè)有1個服務(wù)消費者面對10個提供者,這時面臨第一個問題就是「選哪一個」進(jìn)行調(diào)用,所以負(fù)載均衡最先調(diào)用,假設(shè)選定了5號服務(wù)提供者進(jìn)行服務(wù)調(diào)用。

          假設(shè)消費者調(diào)用5號提供者發(fā)生了超時異常,這時面臨第二個問題就是「換哪一個」進(jìn)行調(diào)用:5號超時要不要換1號試一試,或者直接返回不進(jìn)行重試,所以集群容錯第二個調(diào)用。

          假設(shè)已經(jīng)重試了1號、3號、6號提供者全部超時,這時面臨「全錯怎么辦」這第三個問題,這時可以直接返回一個固定值或者提示文案,所以服務(wù)降級第三個調(diào)用。

          負(fù)載均衡作為整個鏈路第一個節(jié)點非常重要,本文結(jié)合DUBBO源碼分析以下七種負(fù)載均衡策略:

          • 簡單隨機
          • 加權(quán)隨機
          • 簡單輪詢
          • 簡單加權(quán)輪詢
          • 平滑加權(quán)輪詢
          • 一致性哈希
          • 最少活躍數(shù)

          2 簡單隨機

          簡單隨機含義是服務(wù)消費者每次會任意訪問一個服務(wù)提供者,并且從概率角度看每個提供者被訪問概率一致,可以通過指定范圍隨機數(shù)實現(xiàn)。第一步編寫服務(wù)器代碼

          public?class?MyServer?{

          ????private?String?ip;

          ????public?MyServer(String?ip)?{
          ????????this.ip?=?ip;
          ????}

          ????public?String?getIp()?{
          ????????return?ip;
          ????}

          ????public?void?setIp(String?ip)?{
          ????????this.ip?=?ip;
          ????}
          }

          第二步編寫基礎(chǔ)負(fù)載均衡策略

          public?abstract?class?AbstractLoadBalance?{

          ????public?MyServer?select(List?serverList)?{
          ????????return?doSelect(serverList);
          ????}

          ????public?abstract?MyServer?doSelect(List?serverList);
          }

          第三步編寫簡單隨機策略

          public?class?RandomBalance?extends?AbstractLoadBalance?{

          ????@Override
          ????public?MyServer?doSelect(List?serverList)?{
          ????????//?隨機數(shù)范圍[0,serverListSize)
          ????????int?index?=?ThreadLocalRandom.current().nextInt(serverList.size());
          ????????return?serverList.get(index);
          ????}
          }

          第四步編寫測試代碼

          public?class?LoadBalanceTest?{

          ????public?static?void?main(String[]?args)?{
          ????????List?serverList?=?buildData();
          ????????testRandomBalance(serverList);
          ????}

          ????public?static?void?testRandomBalance(List?serverList)?{
          ????????AbstractLoadBalance?randomBalance?=?new?RandomBalance();
          ????????for?(int?i?=?0;?i?10;?i++)?{
          ????????????MyServer?server?=?randomBalance.select(serverList);
          ????????????System.out.println("RandomBalance?route?server="?+?server);
          ????????}
          ????}

          ????public?static?List?buildData()?{
          ????????List?serverList?=?new?ArrayList();
          ????????MyServer?server1?=?new?MyServer("192.1.1.1");
          ????????MyServer?server2?=?new?MyServer("192.1.1.2");
          ????????MyServer?server3?=?new?MyServer("192.1.1.3");
          ????????serverList.add(server1);
          ????????serverList.add(server2);
          ????????serverList.add(server3);
          ????????return?serverList;
          ????}
          }

          第五步輸出結(jié)果,循環(huán)次數(shù)越多結(jié)果越準(zhǔn)確

          RandomBalance route server=MyServer(ip=192.1.1.2)
          RandomBalance route server=MyServer(ip=192.1.1.1)
          RandomBalance route server=MyServer(ip=192.1.1.3)
          RandomBalance route server=MyServer(ip=192.1.1.2)
          RandomBalance route server=MyServer(ip=192.1.1.1)
          RandomBalance route server=MyServer(ip=192.1.1.1)
          RandomBalance route server=MyServer(ip=192.1.1.2)
          RandomBalance route server=MyServer(ip=192.1.1.2)
          RandomBalance route server=MyServer(ip=192.1.1.3)
          RandomBalance route server=MyServer(ip=192.1.1.3)

          3 加權(quán)隨機

          3.1 設(shè)計思路

          加權(quán)隨機新增了權(quán)重概念,假設(shè)服務(wù)器A權(quán)重等于1,服務(wù)器B權(quán)重等于5,從概率角度看B服務(wù)器被訪問概率5倍于A服務(wù)器。實現(xiàn)按照權(quán)重訪問有很多種方式,我們選擇使用概率區(qū)間這個思路。

          假設(shè)現(xiàn)在有三臺服務(wù)器,服務(wù)器權(quán)重分別是3、5、2,那么三者構(gòu)成如下圖概率區(qū)間:




          假設(shè)隨機數(shù)等于8,其位于[8,9]區(qū)間,所以選擇server3,下圖說明了概率區(qū)間計算步驟:



          3.2 代碼實例

          第一步編寫服務(wù)器代碼

          public?class?MyServer?{

          ????private?String?ip;

          ????private?int?weight;

          ????public?MyServer(String?ip)?{
          ????????this.ip?=?ip;
          ????}

          ????public?String?getIp()?{
          ????????return?ip;
          ????}

          ????public?void?setIp(String?ip)?{
          ????????this.ip?=?ip;
          ????}

          ????public?int?getWeight()?{
          ????????return?weight;
          ????}

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

          第二步編寫加權(quán)隨機策略

          public?class?RandomWeightBalance?extends?AbstractLoadBalance?{

          ????@Override
          ????public?MyServer?doSelect(List?serverList)?{
          ????????//?所有服務(wù)器總權(quán)重
          ????????int?totalWeight?=?0;
          ????????//?第一個服務(wù)器權(quán)重
          ????????int?firstWeight?=?serverList.get(0).getWeight();
          ????????//?所有服務(wù)器權(quán)重相等
          ????????boolean?sameWeight?=?true;
          ????????//?遍歷所有服務(wù)器
          ????????for?(MyServer?server?:?serverList)?{
          ????????????//?計算總權(quán)重
          ????????????totalWeight?+=?server.getWeight();
          ????????????//?任意一個invoker權(quán)重不等于第一個權(quán)重則設(shè)置sameWeight=false
          ????????????if?(sameWeight?&&?server.getWeight()?!=?firstWeight)?{
          ????????????????sameWeight?=?false;
          ????????????}
          ????????}
          ????????//?權(quán)重不相等則根據(jù)權(quán)重選擇
          ????????if?(!sameWeight)?{
          ????????????//?在總區(qū)間范圍[0,totalWeight)生成隨機數(shù)A
          ????????????Integer?offset?=?ThreadLocalRandom.current().nextInt(totalWeight);
          ????????????//?遍歷所有服務(wù)器區(qū)間
          ????????????for?(MyServer?server?:?serverList)?{
          ????????????????//?如果A在server區(qū)間直接返回
          ????????????????if?(offset?????????????????????return?server;
          ????????????????}
          ????????????????//?如果A不在server區(qū)間則減去此區(qū)間范圍并繼續(xù)匹配其它區(qū)間
          ????????????????offset?-=?server.getWeight();
          ????????????}
          ????????}
          ????????//?所有服務(wù)器權(quán)重相等則隨機選擇
          ????????return?serverList.get(ThreadLocalRandom.current().nextInt(serverList.size()));
          ????}
          }

          第三步編寫測試代碼

          public?class?LoadBalanceTest?{

          ????public?static?void?main(String[]?args)?{
          ????????List?serverList?=?buildData();
          ????????testRandomWeightBalance(serverList);
          ????}

          ????public?static?void?testRandomWeightBalance(List?serverList)?{
          ????????AbstractLoadBalance?randomBalance?=?new?RandomWeightBalance();
          ????????for?(int?i?=?0;?i?10;?i++)?{
          ????????????MyServer?server?=?randomBalance.select(serverList);
          ????????????System.out.println("RandomWeightBalance?route?server="?+?server);
          ????????}
          ????}

          ????public?static?List?buildData()?{
          ????????List?serverList?=?new?ArrayList();
          ????????MyServer?server1?=?new?MyServer("192.1.1.1",?3);
          ????????MyServer?server2?=?new?MyServer("192.1.1.2",?5);
          ????????MyServer?server3?=?new?MyServer("192.1.1.3",?2);
          ????????serverList.add(server1);
          ????????serverList.add(server2);
          ????????serverList.add(server3);
          ????????return?serverList;
          ????}
          }

          第四步輸出結(jié)果,循環(huán)次數(shù)越多結(jié)果越準(zhǔn)確

          RandomWeightBalance route server=MyServer(ip=192.1.1.2, weight=2)
          RandomWeightBalance route server=MyServer(ip=192.1.1.2, weight=3)
          RandomWeightBalance route server=MyServer(ip=192.1.1.1, weight=3)
          RandomWeightBalance route server=MyServer(ip=192.1.1.1, weight=3)
          RandomWeightBalance route server=MyServer(ip=192.1.1.3, weight=2)
          RandomWeightBalance route server=MyServer(ip=192.1.1.3, weight=2)
          RandomWeightBalance route server=MyServer(ip=192.1.1.2, weight=5)
          RandomWeightBalance route server=MyServer(ip=192.1.1.1, weight=3)
          RandomWeightBalance route server=MyServer(ip=192.1.1.2, weight=5)
          RandomWeightBalance route server=MyServer(ip=192.1.1.2, weight=5)

          3.3 DUBBO源碼

          public?class?RandomLoadBalance?extends?AbstractLoadBalance?{
          ????public?static?final?String?NAME?=?"random";

          ????@Override
          ????protected??Invoker?doSelect(List>?invokers,?URL?url,?Invocation?invocation)?{

          ????????//?invoker數(shù)量
          ????????int?length?=?invokers.size();

          ????????//?所有權(quán)重是否相等
          ????????boolean?sameWeight?=?true;

          ????????//?權(quán)重數(shù)組
          ????????int[]?weights?=?new?int[length];

          ????????//?第一個權(quán)重
          ????????int?firstWeight?=?getWeight(invokers.get(0),?invocation);
          ????????weights[0]?=?firstWeight;

          ????????//?權(quán)重之和
          ????????int?totalWeight?=?firstWeight;

          ????????//?遍歷所有invoker
          ????????for?(int?i?=?1;?i?
          ????????????//?獲取權(quán)重
          ????????????int?weight?=?getWeight(invokers.get(i),?invocation);
          ????????????weights[i]?=?weight;

          ????????????//?計算總權(quán)重
          ????????????totalWeight?+=?weight;

          ????????????//?任意一個invoker權(quán)重不等于第一個權(quán)重則設(shè)置sameWeight=false
          ????????????if?(sameWeight?&&?weight?!=?firstWeight)?{
          ????????????????sameWeight?=?false;
          ????????????}
          ????????}
          ????????//?權(quán)重不相等則根據(jù)權(quán)重選擇
          ????????if?(totalWeight?>?0?&&?!sameWeight)?{
          ????????????int?offset?=?ThreadLocalRandom.current().nextInt(totalWeight);
          ????????????for?(int?i?=?0;?i?????????????????offset?-=?weights[i];
          ????????????????if?(offset?0)?{
          ????????????????????return?invokers.get(i);
          ????????????????}
          ????????????}
          ????????}
          ????????//?所有服務(wù)權(quán)重相等則隨機選擇
          ????????return?invokers.get(ThreadLocalRandom.current().nextInt(length));
          ????}
          }

          4 簡單輪詢

          簡單輪詢含義是服務(wù)消費者每次會依次訪問一個服務(wù)提供者,并且從概率角度看每個提供者被訪問概率一致,可以通過原子變量累加實現(xiàn)。第一步編寫簡單輪詢策略

          public?class?RoundRobinBalance?extends?AbstractLoadBalance?{
          ????private?AtomicInteger?atomicIndex?=?new?AtomicInteger(0);

          ????@Override
          ????public?MyServer?doSelect(List?serverList)?{
          ????????//?atomicIndex自增大于服務(wù)器數(shù)量時取余
          ????????int?index?=?atomicIndex.getAndIncrement()?%?serverList.size();
          ????????return?serverList.get(index);
          ????}
          }

          第二步編寫測試代碼

          public?class?LoadBalanceTest?{

          ????public?static?void?main(String[]?args)?{
          ????????List?serverList?=?buildData();
          ????????testRoundRobinBalance(serverList);
          ????}

          ????public?static?void?testRoundRobinBalance(List?serverList)?{
          ????????AbstractLoadBalance?roundRobinBalance?=?new?RoundRobinBalance();
          ????????for?(int?i?=?0;?i?10;?i++)?{
          ????????????MyServer?server?=?roundRobinBalance.select(serverList);
          ????????????System.out.println("RoundRobinBalance?route?server="?+?server);
          ????????}
          ????}

          ????public?static?List?buildData()?{
          ????????List?serverList?=?new?ArrayList();
          ????????MyServer?server1?=?new?MyServer("192.1.1.1");
          ????????MyServer?server2?=?new?MyServer("192.1.1.2");
          ????????MyServer?server3?=?new?MyServer("192.1.1.3");
          ????????serverList.add(server1);
          ????????serverList.add(server2);
          ????????serverList.add(server3);
          ????????return?serverList;
          ????}
          }

          第三步輸出結(jié)果

          RoundRobinBalance route server=MyServer(ip=192.1.1.1)
          RoundRobinBalance route server=MyServer(ip=192.1.1.2)
          RoundRobinBalance route server=MyServer(ip=192.1.1.3)
          RoundRobinBalance route server=MyServer(ip=192.1.1.1)
          RoundRobinBalance route server=MyServer(ip=192.1.1.2)
          RoundRobinBalance route server=MyServer(ip=192.1.1.3)
          RoundRobinBalance route server=MyServer(ip=192.1.1.1)
          RoundRobinBalance route server=MyServer(ip=192.1.1.2)
          RoundRobinBalance route server=MyServer(ip=192.1.1.3)
          RoundRobinBalance route server=MyServer(ip=192.1.1.1)

          5 簡單加權(quán)輪詢

          簡單加權(quán)輪詢新增了權(quán)重概念,假設(shè)服務(wù)器A權(quán)重等于1,服務(wù)器B權(quán)重等于5,從概率角度看B服務(wù)器被訪問概率5倍于A服務(wù)器,我們還是使用概率區(qū)間這個思路,相較于加權(quán)隨機會有一些變化。



          第一步編寫簡單加權(quán)輪詢策略

          public?class?RoundRobinWeightBalance1?extends?AbstractLoadBalance?{
          ????private?AtomicInteger?atomicIndex?=?new?AtomicInteger(0);

          ????@Override
          ????public?MyServer?doSelect(List?serverList)?{
          ????????int?totalWeight?=?0;
          ????????int?firstWeight?=?serverList.get(0).getWeight();
          ????????boolean?sameWeight?=?true;
          ????????for?(MyServer?server?:?serverList)?{
          ????????????totalWeight?+=?server.getWeight();
          ????????????if?(sameWeight?&&?server.getWeight()?!=?firstWeight)?{
          ????????????????sameWeight?=?false;
          ????????????}
          ????????}
          ????????if?(!sameWeight)?{
          ????????????//?自增方式計算offset
          ????????????int?offset?=?atomicIndex.getAndIncrement()?%?totalWeight;
          ????????????for?(MyServer?server?:?serverList)?{
          ????????????????if?(offset?????????????????????return?server;
          ????????????????}
          ????????????????offset?-=?server.getWeight();
          ????????????}
          ????????}
          ????????int?index?=?atomicIndex.getAndIncrement()?%?serverList.size();
          ????????return?serverList.get(index);
          ????}
          }

          第二步編寫測試代碼

          public?class?LoadBalanceTest?{

          ????public?static?void?main(String[]?args)?{
          ????????List?serverList?=?buildData();
          ????????testRoundRobinWeightBalance1(serverList);
          ????}

          ????public?static?void?testRoundRobinWeightBalance1(List?serverList)?{
          ????????AbstractLoadBalance?roundRobinBalance?=?new?RoundRobinWeightBalance1();
          ????????for?(int?i?=?0;?i?10;?i++)?{
          ????????????MyServer?server?=?roundRobinBalance.select(serverList);
          ????????????System.out.println("RoundRobinWeightBalance1?route?server="?+?server);
          ????????}
          ????}

          ????public?static?List?buildData()?{
          ????????List?serverList?=?new?ArrayList();
          ????????MyServer?server1?=?new?MyServer("192.1.1.1",?3);
          ????????MyServer?server2?=?new?MyServer("192.1.1.2",?5);
          ????????MyServer?server3?=?new?MyServer("192.1.1.3",?2);
          ????????serverList.add(server1);
          ????????serverList.add(server2);
          ????????serverList.add(server3);
          ????????return?serverList;
          ????}
          }

          第三步輸出結(jié)果

          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.1, weight=3)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.1, weight=3)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.1, weight=3)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.2, weight=5)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.2, weight=5)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.2, weight=5)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.2, weight=5)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.2, weight=5)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.3, weight=2)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.3, weight=2)

          6 平滑加權(quán)輪詢

          6.1 設(shè)計思路

          簡單加權(quán)輪詢有什么問題?我們分析其輸出結(jié)果發(fā)現(xiàn),連續(xù)3次訪問服務(wù)器1,連續(xù)5次訪問服務(wù)器2,連續(xù)2次訪問服務(wù)器3,所以簡單加權(quán)輪詢策略會導(dǎo)致請求集中問題。

          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.1, weight=3)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.1, weight=3)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.1, weight=3)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.2, weight=5)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.2, weight=5)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.2, weight=5)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.2, weight=5)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.2, weight=5)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.3, weight=2)
          RoundRobinWeightBalance1 route server=MyServer(ip=192.1.1.3, weight=2)

          所以需要使用平滑加權(quán)輪詢策略,將請求比較均勻地分散至各個服務(wù)器,下圖說明了計算步驟:



          6.2 代碼實例

          第一步編寫服務(wù)器代碼

          public?class?MyServer?{

          ????private?String?ip;

          ????private?int?weight;

          ????private?int?currentWeight?=?0;

          ????public?MyServer(String?ip)?{
          ????????this.ip?=?ip;
          ????}

          ????public?MyServer(String?ip,?int?weight)?{
          ????????this.ip?=?ip;
          ????????this.weight?=?weight;
          ????}

          ????public?int?getWeight()?{
          ????????return?weight;
          ????}

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

          ????public?int?getCurrentWeight()?{
          ????????return?currentWeight;
          ????}

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

          ????public?String?getIp()?{
          ????????return?ip;
          ????}

          ????public?void?setIp(String?ip)?{
          ????????this.ip?=?ip;
          ????}
          }

          第二步編寫平滑加權(quán)輪詢策略

          public?class?RoundRobinWeightBalance2?extends?AbstractLoadBalance?{
          ????private?AtomicInteger?atomicIndex?=?new?AtomicInteger(0);

          ????@Override
          ????public?MyServer?doSelect(List?serverList)?{
          ????????int?totalWeight?=?0;
          ????????int?firstWeight?=?serverList.get(0).getWeight();
          ????????boolean?sameWeight?=?true;
          ????????for?(MyServer?server?:?serverList)?{
          ????????????totalWeight?+=?server.getWeight();
          ????????????if?(sameWeight?&&?server.getWeight()?!=?firstWeight)?{
          ????????????????sameWeight?=?false;
          ????????????}
          ????????????//?設(shè)置動態(tài)權(quán)重?->?currentWeight?+=?weight
          ????????????server.setCurrentWeight(server.getCurrentWeight()?+?server.getWeight());
          ????????}
          ????????if?(!sameWeight)?{
          ????????????//?最大動態(tài)權(quán)重服務(wù)器?->?max(currentWeight)
          ????????????MyServer?maxCurrentWeightServer?=?serverList.stream().max((s1,?s2)?->?s1.getCurrentWeight()?-?s2.getCurrentWeight()).get();
          ????????????//?設(shè)置最大動態(tài)權(quán)重?->?max(currentWeight)?-?totalWeight
          ????????????maxCurrentWeightServer.setCurrentWeight(maxCurrentWeightServer.getCurrentWeight()?-?totalWeight);
          ????????????//?返回最大動態(tài)權(quán)重服務(wù)器
          ????????????return?maxCurrentWeightServer;
          ????????}
          ????????//?權(quán)重相同依次輪詢
          ????????int?index?=?atomicIndex.getAndIncrement()?%?serverList.size();
          ????????return?serverList.get(index);
          ????}
          }

          第三步編寫測試代碼

          public?class?LoadBalanceTest?{

          ????public?static?void?main(String[]?args)?{
          ????????List?serverList?=?buildData();
          ????????testRoundRobinWeightBalance2(serverList);
          ????}

          ????public?static?void?testRoundRobinWeightBalance2(List?serverList)?{
          ????????AbstractLoadBalance?roundRobinBalance?=?new?RoundRobinWeightBalance2();
          ????????for?(int?i?=?0;?i?10;?i++)?{
          ????????????MyServer?server?=?roundRobinBalance.select(serverList);
          ????????????System.out.println("RoundRobinWeightBalance2?route?server="?+?server);
          ????????}
          ????}

          ????public?static?List?buildData()?{
          ????????List?serverList?=?new?ArrayList();
          ????????MyServer?server1?=?new?MyServer("192.1.1.1",?3);
          ????????MyServer?server2?=?new?MyServer("192.1.1.2",?5);
          ????????MyServer?server3?=?new?MyServer("192.1.1.3",?2);
          ????????serverList.add(server1);
          ????????serverList.add(server2);
          ????????serverList.add(server3);
          ????????return?serverList;
          ????}
          }

          第四步輸出結(jié)果

          RoundRobinWeightBalance2 route server=MyServer(ip=192.1.1.2, weight=5, currentWeight=-5)
          RoundRobinWeightBalance2 route server=MyServer(ip=192.1.1.1, weight=3, currentWeight=-4)
          RoundRobinWeightBalance2 route server=MyServer(ip=192.1.1.3, weight=2, currentWeight=-4)
          RoundRobinWeightBalance2 route server=MyServer(ip=192.1.1.2, weight=5, currentWeight=0)
          RoundRobinWeightBalance2 route server=MyServer(ip=192.1.1.1, weight=3, currentWeight=-5)
          RoundRobinWeightBalance2 route server=MyServer(ip=192.1.1.2, weight=5, currentWeight=0)
          RoundRobinWeightBalance2 route server=MyServer(ip=192.1.1.2, weight=5, currentWeight=-5)
          RoundRobinWeightBalance2 route server=MyServer(ip=192.1.1.3, weight=2, currentWeight=-4)
          RoundRobinWeightBalance2 route server=MyServer(ip=192.1.1.1, weight=3, currentWeight=-3)
          RoundRobinWeightBalance2 route server=MyServer(ip=192.1.1.2, weight=5, currentWeight=0)

          6.3 DUBBO源碼

          public?class?RoundRobinLoadBalance?extends?AbstractLoadBalance?{
          ????public?static?final?String?NAME?=?"roundrobin";

          ????private?static?int?RECYCLE_PERIOD?=?60000;

          ????protected?static?class?WeightedRoundRobin?{

          ????????//?權(quán)重
          ????????private?int?weight;

          ????????//?動態(tài)權(quán)重
          ????????private?AtomicLong?current?=?new?AtomicLong(0);

          ????????//?更新時間
          ????????private?long?lastUpdate;

          ????????public?int?getWeight()?{
          ????????????return?weight;
          ????????}

          ????????public?void?setWeight(int?weight)?{
          ????????????this.weight?=?weight;
          ????????????current.set(0);
          ????????}

          ????????public?long?increaseCurrent()?{
          ????????????return?current.addAndGet(weight);
          ????????}

          ????????public?void?sel(int?total)?{
          ????????????current.addAndGet(-1?*?total);
          ????????}

          ????????public?long?getLastUpdate()?{
          ????????????return?lastUpdate;
          ????????}

          ????????public?void?setLastUpdate(long?lastUpdate)?{
          ????????????this.lastUpdate?=?lastUpdate;
          ????????}
          ????}

          ????private?ConcurrentMap>?methodWeightMap?=?new?ConcurrentHashMap>();
          ????private?AtomicBoolean?updateLock?=?new?AtomicBoolean();

          ????protected??Collection?getInvokerAddrList(List>?invokers,?Invocation?invocation)?{
          ????????String?key?=?invokers.get(0).getUrl().getServiceKey()?+?"."?+?invocation.getMethodName();
          ????????Map?map?=?methodWeightMap.get(key);
          ????????if?(map?!=?null)?{
          ????????????return?map.keySet();
          ????????}
          ????????return?null;
          ????}

          ????@Override
          ????protected??Invoker?doSelect(List>?invokers,?URL?url,?Invocation?invocation)?{
          ????????String?key?=?invokers.get(0).getUrl().getServiceKey()?+?"."?+?invocation.getMethodName();
          ????????ConcurrentMap?map?=?methodWeightMap.get(key);
          ????????if?(map?==?null)?{
          ????????????methodWeightMap.putIfAbsent(key,?new?ConcurrentHashMap());
          ????????????map?=?methodWeightMap.get(key);
          ????????}
          ????????//?總權(quán)重
          ????????int?totalWeight?=?0;

          ????????//?最大當(dāng)前權(quán)重
          ????????long?maxCurrent?=?Long.MIN_VALUE;

          ????????//?當(dāng)前時間
          ????????long?now?=?System.currentTimeMillis();

          ????????//?選中提供者
          ????????Invoker?selectedInvoker?=?null;

          ????????//?選中提供者權(quán)重對象
          ????????WeightedRoundRobin?selectedWRR?=?null;

          ????????//?遍歷所有提供者
          ????????for?(Invoker?invoker?:?invokers)?{
          ????????????String?identifyString?=?invoker.getUrl().toIdentityString();
          ????????????WeightedRoundRobin?weightedRoundRobin?=?map.get(identifyString);

          ????????????//?獲取權(quán)重
          ????????????int?weight?=?getWeight(invoker,?invocation);
          ????????????if?(weightedRoundRobin?==?null)?{
          ????????????????weightedRoundRobin?=?new?WeightedRoundRobin();
          ????????????????weightedRoundRobin.setWeight(weight);
          ????????????????map.putIfAbsent(identifyString,?weightedRoundRobin);
          ????????????}
          ????????????if?(weight?!=?weightedRoundRobin.getWeight())?{
          ????????????????weightedRoundRobin.setWeight(weight);
          ????????????}
          ????????????//?選擇動態(tài)權(quán)重最大提供者
          ????????????long?cur?=?weightedRoundRobin.increaseCurrent();
          ????????????weightedRoundRobin.setLastUpdate(now);
          ????????????if?(cur?>?maxCurrent)?{
          ????????????????maxCurrent?=?cur;
          ????????????????selectedInvoker?=?invoker;
          ????????????????selectedWRR?=?weightedRoundRobin;
          ????????????}
          ????????????//?計算總權(quán)重
          ????????????totalWeight?+=?weight;
          ????????}
          ????????//?更新負(fù)載均衡容器
          ????????if?(!updateLock.get()?&&?invokers.size()?!=?map.size())?{
          ????????????if?(updateLock.compareAndSet(false,?true))?{
          ????????????????try?{
          ????????????????????ConcurrentMap?newMap?=?new?ConcurrentHashMap();
          ????????????????????newMap.putAll(map);
          ????????????????????Iterator>?it?=?newMap.entrySet().iterator();
          ????????????????????while?(it.hasNext())?{
          ????????????????????????Entry?item?=?it.next();
          ????????????????????????if?(now?-?item.getValue().getLastUpdate()?>?RECYCLE_PERIOD)?{
          ????????????????????????????it.remove();
          ????????????????????????}
          ????????????????????}
          ????????????????????methodWeightMap.put(key,?newMap);
          ????????????????}?finally?{
          ????????????????????updateLock.set(false);
          ????????????????}
          ????????????}
          ????????}
          ????????if?(selectedInvoker?!=?null)?{
          ????????????//?最大動態(tài)權(quán)重減去總權(quán)重
          ????????????selectedWRR.sel(totalWeight);
          ????????????return?selectedInvoker;
          ????????}
          ????????return?invokers.get(0);
          ????}
          }

          7 一致性哈希

          一致性哈希策略具有三個顯著特性:第一是在不新增或者刪除提供者情況下,同一個客戶端總是可以訪問到同一個提供者。第二是一致性哈希可以有效分散新增或者刪除服務(wù)提供者帶來的波動性。第三是一致性哈希虛擬節(jié)點可以更加有效分散特性二之波動性。


          7.1 特性分析

          (1) 特性一

          在不新增或者刪除提供者情況下,同一個客戶端總是可以訪問同一個提供者。第一步6個提供者分布在哈希環(huán)中,第二步client1發(fā)起訪問請求,此時計算客戶端哈希值位于哈希環(huán)位置,第三步沿著哈希環(huán)順時針旋轉(zhuǎn),找到與客戶端哈希值最近的提供者server5。當(dāng)哈希環(huán)結(jié)構(gòu)不發(fā)生改變時,client1總是路由到server5。



          哈希環(huán)應(yīng)該選擇什么數(shù)據(jù)結(jié)構(gòu)呢?我們可以選擇TreeMap構(gòu)建哈希環(huán),其底層使用了紅黑樹。

          public?class?TreeMapTest?{

          ????public?static?void?main(String[]?args)?{
          ????
          ????????//?構(gòu)建哈希環(huán)
          ????????TreeMap?treeMap?=?new?TreeMap();
          ????????treeMap.put(1,?new?MyServer("1"));
          ????????treeMap.put(2,?new?MyServer("2"));
          ????????treeMap.put(3,?new?MyServer("3"));
          ????????treeMap.put(4,?new?MyServer("4"));
          ????????treeMap.put(5,?new?MyServer("5"));
          ????????treeMap.put(6,?new?MyServer("6"));

          ????????//?找到第一個大于客戶端哈希值的服務(wù)器
          ????????Integer?clientHashCode?=?5;
          ????????SortedMap?tailMap?=?treeMap.tailMap(clientHashCode,?false);
          ????????MyServer?server?=?tailMap.get(tailMap.firstKey());

          ????????//?MyServer(ip=6)
          ????????System.out.println(server);
          ????}
          }

          (2) 特性二

          一致性哈希可以有效分散新增或者刪除提供者帶來的波動性,例如新增服務(wù)器server7,但是并不影響client1路由結(jié)果:



          服務(wù)器server5發(fā)生宕機只會影響client1路由結(jié)果,并不會影響其它客戶端路由結(jié)果:



          (3) 特性三

          一致性哈希虛擬節(jié)點可以更加有效分散特性二之波動性,例如我們可以為每個服務(wù)器節(jié)點新增一個虛擬節(jié)點,使得服務(wù)器分散得更加均勻:



          7.2 代碼實例

          第一步編寫基礎(chǔ)負(fù)載均衡策略

          public?abstract?class?AbstractConsistentHashLoadBalance?{

          ????public?MyServer?select(String?clientIP,?List?serverList)?{
          ????????return?doSelect(clientIP,?serverList);
          ????}

          ????public?abstract?MyServer?doSelect(String?clientIP,?List?serverList);
          }

          第二步編寫一致性哈希策略

          public?class?ConsistentHashBalance1?extends?AbstractConsistentHashLoadBalance?{
          ????private?ConsistentHashSelector?consistentHashSelector;

          ????@Override
          ????public?MyServer?doSelect(String?clientIP,?List?serverList)?{
          ????????initialConsistentHashSelector(serverList);
          ????????return?consistentHashSelector.select(clientIP);
          ????}

          ????private?class?ConsistentHashSelector?{
          ????????private?Integer?identityHashCode;
          ????????private?TreeMap/*?hashcode?*/,?MyServer>?serverNodes?=?new?TreeMap();

          ????????//?構(gòu)建哈希環(huán)
          ????????public?ConsistentHashSelector(Integer?identityHashCode,?List?serverList)?{
          ????????????this.identityHashCode?=?identityHashCode;
          ????????????TreeMap?newServerNodes?=?new?TreeMap();
          ????????????for?(MyServer?server?:?serverList)?{
          ????????????????newServerNodes.put(hashCode(server.getIp()),?server);
          ????????????}
          ????????????serverNodes?=?newServerNodes;
          ????????}

          ????????//?根據(jù)客戶端IP路由
          ????????public?MyServer?select(String?clientIP)?{

          ????????????//?計算客戶端哈希值
          ????????????int?clientHashCode?=?hashCode(clientIP);

          ????????????//?找到第一個大于客戶端哈希值的服務(wù)器
          ????????????SortedMap?tailMap?=?serverNodes.tailMap(clientHashCode,?false);
          ????????????if?(CollectionUtils.isEmpty(tailMap))?{
          ????????????????Integer?firstKey?=?serverNodes.firstKey();
          ????????????????return?serverNodes.get(firstKey);
          ????????????}

          ????????????//?找不到表示在最后一個節(jié)點和第一個節(jié)點之間?->選擇第一個節(jié)點
          ????????????Integer?firstKey?=?tailMap.firstKey();
          ????????????return?tailMap.get(firstKey);
          ????????}

          ????????//?計算哈希值
          ????????private?int?hashCode(String?key)?{
          ????????????return?Objects.hashCode(key);
          ????????}

          ????????//?提供者列表哈希值?->?如果新增或者刪除提供者會發(fā)生變化
          ????????public?Integer?getIdentityHashCode()?{
          ????????????return?identityHashCode;
          ????????}
          ????}

          ????private?void?initialConsistentHashSelector(List?serverList)?{

          ????????//?計算提供者列表哈希值
          ????????Integer?newIdentityHashCode?=?System.identityHashCode(serverList);

          ????????//?提供者列表哈希值沒有變化則無需重新構(gòu)建哈希環(huán)
          ????????if?(null?!=?consistentHashSelector?&&?(null?!=?consistentHashSelector.getIdentityHashCode()?&&?newIdentityHashCode?==?consistentHashSelector.getIdentityHashCode()))?{
          ????????????return;
          ????????}
          ????????//?提供者列表哈希值發(fā)生變化則重新構(gòu)建哈希環(huán)
          ????????consistentHashSelector?=?new?ConsistentHashSelector(newIdentityHashCode,?serverList);
          ????}
          }

          第三步編寫測試代碼

          public?class?LoadBalanceTest?{

          ????public?static?void?main(String[]?args)?{
          ????????testConsistentHashBalance1();
          ????}

          ????public?static?void?testConsistentHashBalance1()?{
          ????????List?serverList?=?new?ArrayList();
          ????????MyServer?server1?=?new?MyServer("1");
          ????????MyServer?server2?=?new?MyServer("2");
          ????????MyServer?server3?=?new?MyServer("3");
          ????????MyServer?server4?=?new?MyServer("4");
          ????????MyServer?server5?=?new?MyServer("5");
          ????????MyServer?server6?=?new?MyServer("6");
          ????????serverList.add(server1);
          ????????serverList.add(server2);
          ????????serverList.add(server3);
          ????????serverList.add(server4);
          ????????serverList.add(server5);
          ????????serverList.add(server6);
          ????????AbstractConsistentHashLoadBalance?consistentHashBalance?=?new?ConsistentHashBalance1();
          ????????for?(int?i?=?0;?i?10;?i++)?{
          ????????????String?clientIP?=?"5";
          ????????????MyServer?server?=?consistentHashBalance.select(clientIP,?serverList);
          ????????????System.out.println("clientIP="?+?clientIP?+?",consistentHashBalance1?route?server="?+?server);
          ????????}
          ????}
          }

          第四步輸出結(jié)果

          clientIP=5,consistentHashBalance1 route server=MyServer(ip=6)
          clientIP=5,consistentHashBalance1 route server=MyServer(ip=6)
          clientIP=5,consistentHashBalance1 route server=MyServer(ip=6)
          clientIP=5,consistentHashBalance1 route server=MyServer(ip=6)
          clientIP=5,consistentHashBalance1 route server=MyServer(ip=6)
          clientIP=5,consistentHashBalance1 route server=MyServer(ip=6)
          clientIP=5,consistentHashBalance1 route server=MyServer(ip=6)
          clientIP=5,consistentHashBalance1 route server=MyServer(ip=6)
          clientIP=5,consistentHashBalance1 route server=MyServer(ip=6)
          clientIP=5,consistentHashBalance1 route server=MyServer(ip=6)

          如果新增虛擬節(jié)點參看以下代碼

          public?class?ConsistentHashBalance2?extends?AbstractConsistentHashLoadBalance?{
          ????private?ConsistentHashSelector?consistentHashSelector;

          ????@Override
          ????public?MyServer?doSelect(String?clientIP,?List?serverList)?{
          ????????initialSelector(serverList);
          ????????return?consistentHashSelector.select(clientIP);
          ????}

          ????private?class?ConsistentHashSelector?{
          ????????private?Integer?identityHashCode;
          ????????private?Integer?VIRTUAL_NODES_NUM?=?16;
          ????????private?TreeMap/*?hashcode?*/,?MyServer>?serverNodes?=?new?TreeMap();

          ????????public?ConsistentHashSelector(Integer?identityHashCode,?List?serverList)?{
          ????????????this.identityHashCode?=?identityHashCode;
          ????????????TreeMap?newServerNodes?=?new?TreeMap();
          ????????????for?(MyServer?server?:?serverList)?{
          ????????????????//?虛擬節(jié)點
          ????????????????for?(int?i?=?0;?i?????????????????????int?virtualKey?=?hashCode(server.getIp()?+?"_"?+?i);
          ????????????????????newServerNodes.put(virtualKey,?server);
          ????????????????}
          ????????????}
          ????????????serverNodes?=?newServerNodes;
          ????????}

          ????????public?MyServer?select(String?clientIP)?{
          ????????????int?clientHashCode?=?hashCode(clientIP);
          ????????????SortedMap?tailMap?=?serverNodes.tailMap(clientHashCode,?false);
          ????????????if?(CollectionUtils.isEmpty(tailMap))?{
          ????????????????Integer?firstKey?=?serverNodes.firstKey();
          ????????????????return?serverNodes.get(firstKey);
          ????????????}
          ????????????Integer?firstKey?=?tailMap.firstKey();
          ????????????return?tailMap.get(firstKey);
          ????????}

          ????????private?int?hashCode(String?key)?{
          ????????????return?Objects.hashCode(key);
          ????????}

          ????????public?Integer?getIdentityHashCode()?{
          ????????????return?identityHashCode;
          ????????}
          ????}

          ????private?void?initialSelector(List?serverList)?{
          ????????Integer?newIdentityHashCode?=?System.identityHashCode(serverList);
          ????????if?(null?!=?consistentHashSelector?&&?(null?!=?consistentHashSelector.getIdentityHashCode()?&&?newIdentityHashCode?==?consistentHashSelector.getIdentityHashCode()))?{
          ????????????return;
          ????????}
          ????????consistentHashSelector?=?new?ConsistentHashSelector(newIdentityHashCode,?serverList);
          ????}
          }

          7.3 DUBBO源碼

          public?class?ConsistentHashLoadBalance?extends?AbstractLoadBalance?{
          ????public?static?final?String?NAME?=?"consistenthash";
          ????private?final?ConcurrentMap>?selectors?=?new?ConcurrentHashMap>();

          ????@Override
          ????protected??Invoker?doSelect(List>?invokers,?URL?url,?Invocation?invocation)?{
          ????????String?methodName?=?RpcUtils.getMethodName(invocation);
          ????????String?key?=?invokers.get(0).getUrl().getServiceKey()?+?"."?+?methodName;
          ????????int?identityHashCode?=?System.identityHashCode(invokers);
          ????????ConsistentHashSelector?selector?=?(ConsistentHashSelector)?selectors.get(key);

          ????????//?提供者列表哈希值發(fā)生變化則重新構(gòu)建哈希環(huán)
          ????????if?(selector?==?null?||?selector.identityHashCode?!=?identityHashCode)?{
          ????????????selectors.put(key,?new?ConsistentHashSelector(invokers,?methodName,?identityHashCode));
          ????????????selector?=?(ConsistentHashSelector)?selectors.get(key);
          ????????}
          ????????return?selector.select(invocation);
          ????}

          ????private?static?final?class?ConsistentHashSelector<T>?{

          ????????private?final?TreeMap>?virtualInvokers;

          ????????private?final?int?replicaNumber;

          ????????private?final?int?identityHashCode;

          ????????private?final?int[]?argumentIndex;

          ????????//?構(gòu)建哈希環(huán)
          ????????ConsistentHashSelector(List>?invokers,?String?methodName,?int?identityHashCode)?{
          ????????????this.virtualInvokers?=?new?TreeMap>();
          ????????????this.identityHashCode?=?identityHashCode;
          ????????????URL?url?=?invokers.get(0).getUrl();
          ????????????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];
          ????????????for?(int?i?=?0;?i?????????????????argumentIndex[i]?=?Integer.parseInt(index[i]);
          ????????????}
          ????????????for?(Invoker?invoker?:?invokers)?{
          ????????????????String?address?=?invoker.getUrl().getAddress();
          ????????????????//?新增虛擬節(jié)點(默認(rèn)160個)
          ????????????????for?(int?i?=?0;?i?4;?i++)?{
          ????????????????????byte[]?digest?=?md5(address?+?i);
          ????????????????????for?(int?h?=?0;?h?4;?h++)?{
          ????????????????????????long?m?=?hash(digest,?h);
          ????????????????????????virtualInvokers.put(m,?invoker);
          ????????????????????}
          ????????????????}
          ????????????}
          ????????}

          ????????//?負(fù)載均衡
          ????????public?Invoker?select(Invocation?invocation)?{
          ????????????String?key?=?toKey(invocation.getArguments());
          ????????????byte[]?digest?=?md5(key);
          ????????????return?selectForKey(hash(digest,?0));
          ????????}

          ????????private?String?toKey(Object[]?args)?{
          ????????????StringBuilder?buf?=?new?StringBuilder();
          ????????????for?(int?i?:?argumentIndex)?{
          ????????????????if?(i?>=?0?&&?i?????????????????????buf.append(args[i]);
          ????????????????}
          ????????????}
          ????????????return?buf.toString();
          ????????}

          ????????private?Invoker?selectForKey(long?hash)?{
          ????????????Map.Entry>?entry?=?virtualInvokers.ceilingEntry(hash);
          ????????????if?(entry?==?null)?{
          ????????????????entry?=?virtualInvokers.firstEntry();
          ????????????}
          ????????????return?entry.getValue();
          ????????}

          ????????//?哈希運算
          ????????private?long?hash(byte[]?digest,?int?number)?{
          ????????????return?(((long)?(digest[3?+?number?*?4]?&?0xFF)?<24)
          ????????????????????|?((long)?(digest[2?+?number?*?4]?&?0xFF)?<16)
          ????????????????????|?((long)?(digest[1?+?number?*?4]?&?0xFF)?<8)
          ????????????????????|?(digest[number?*?4]?&?0xFF))
          ???????????????????&?0xFFFFFFFFL;
          ????????}

          ????????private?byte[]?md5(String?value)?{
          ????????????MessageDigest?md5;
          ????????????try?{
          ????????????????md5?=?MessageDigest.getInstance("MD5");
          ????????????}?catch?(NoSuchAlgorithmException?e)?{
          ????????????????throw?new?IllegalStateException(e.getMessage(),?e);
          ????????????}
          ????????????md5.reset();
          ????????????byte[]?bytes?=?value.getBytes(StandardCharsets.UTF_8);
          ????????????md5.update(bytes);
          ????????????return?md5.digest();
          ????????}
          ????}
          }

          8 最少活躍數(shù)

          每個提供者維護并發(fā)處理的任務(wù)個數(shù),任務(wù)個數(shù)越大活躍度越高。在服務(wù)消費者進(jìn)行負(fù)載均衡時,第一查詢提供者負(fù)載情況,第二選擇活躍度最低的提供者,我們直接分析DUBBO源碼:

          public?class?LeastActiveLoadBalance?extends?AbstractLoadBalance?{
          ????public?static?final?String?NAME?=?"leastactive";

          ????@Override
          ????protected??Invoker?doSelect(List>?invokers,?URL?url,?Invocation?invocation)?{

          ????????//?invoker數(shù)量
          ????????int?length?=?invokers.size();

          ????????//?最小調(diào)用次數(shù)
          ????????int?leastActive?=?-1;

          ????????//?調(diào)用次數(shù)等于最小次數(shù)invoker數(shù)量
          ????????int?leastCount?=?0;

          ????????//?調(diào)用次數(shù)等于最小調(diào)用次數(shù)invoker下標(biāo)集合
          ????????int[]?leastIndexes?=?new?int[length];

          ????????//?每個服務(wù)提供者權(quán)重
          ????????int[]?weights?=?new?int[length];

          ????????//?總權(quán)重
          ????????int?totalWeight?=?0;

          ????????//?第一個調(diào)用者權(quán)重
          ????????int?firstWeight?=?0;

          ????????//?權(quán)重值是否相同
          ????????boolean?sameWeight?=?true;

          ????????//?遍歷invokers
          ????????for?(int?i?=?0;?i?????????????Invoker?invoker?=?invokers.get(i);

          ????????????//?調(diào)用次數(shù)
          ????????????int?active?=?RpcStatus.getStatus(invoker.getUrl(),?invocation.getMethodName()).getActive();

          ????????????//?獲取權(quán)重
          ????????????int?afterWarmup?=?getWeight(invoker,?invocation);

          ????????????//?設(shè)置權(quán)重
          ????????????weights[i]?=?afterWarmup;

          ????????????//?第一個invoker或者調(diào)用次數(shù)小于最小調(diào)用次數(shù)
          ????????????if?(leastActive?==?-1?||?active?????????????????leastActive?=?active;
          ????????????????leastCount?=?1;
          ????????????????leastIndexes[0]?=?i;
          ????????????????totalWeight?=?afterWarmup;
          ????????????????firstWeight?=?afterWarmup;
          ????????????????sameWeight?=?true;
          ????????????}
          ????????????//?當(dāng)前服務(wù)提供者調(diào)用次數(shù)等于最小調(diào)用次數(shù)
          ????????????else?if?(active?==?leastActive)?{
          ????????????????//?記錄下標(biāo)
          ????????????????leastIndexes[leastCount++]?=?i;
          ????????????????//?新增總權(quán)重值
          ????????????????totalWeight?+=?afterWarmup;
          ????????????????//?權(quán)重值是否相同
          ????????????????if?(sameWeight?&&?i?>?0?&&?afterWarmup?!=?firstWeight)?{
          ????????????????????sameWeight?=?false;
          ????????????????}
          ????????????}
          ????????}

          ????????//?只有一個invoker調(diào)用次數(shù)等于最小調(diào)用次數(shù)直接返回
          ????????if?(leastCount?==?1)?{
          ????????????return?invokers.get(leastIndexes[0]);
          ????????}

          ????????//?多個invoker調(diào)用次數(shù)等于最小調(diào)用次數(shù)并且權(quán)重值不相同->根據(jù)權(quán)重值選擇
          ????????if?(!sameWeight?&&?totalWeight?>?0)?{
          ????????????int?offsetWeight?=?ThreadLocalRandom.current().nextInt(totalWeight);
          ????????????for?(int?i?=?0;?i?????????????????int?leastIndex?=?leastIndexes[i];
          ????????????????offsetWeight?-=?weights[leastIndex];
          ????????????????if?(offsetWeight?0)?{
          ????????????????????return?invokers.get(leastIndex);
          ????????????????}
          ????????????}
          ????????}
          ????????//?多個invoker調(diào)用次數(shù)等于最小調(diào)用次數(shù)并且權(quán)重值相同->隨機選擇
          ????????return?invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
          ????}
          }

          9 文章總結(jié)

          第一本文首先分析了負(fù)載均衡、集群容錯、服務(wù)降級這三組概念,第二結(jié)合代碼分析了簡單隨機,加權(quán)隨機,簡單輪詢,簡單加權(quán)輪詢,平滑加權(quán)輪詢,一致性哈希,最少活躍數(shù)七種負(fù)載均衡策略,其中權(quán)重計算、平滑加權(quán)輪詢,一致性哈希算法值得注意,希望本文對大家有所幫助。



          JAVA前線?


          歡迎大家關(guān)注公眾號「JAVA前線」查看更多精彩分享,主要內(nèi)容包括源碼分析、實際應(yīng)用、架構(gòu)思維、職場分享、產(chǎn)品思考等等,同時也非常歡迎大家加我微信「java_front」一起交流學(xué)習(xí)


          瀏覽 42
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人在线小视频 | 蜜臀AV一区二区三区有限公司 | 日韩黄色电影视频 | 丁香六月婷婷综合 | 无码精品人妻 |