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

        共 3233字,需瀏覽 7分鐘

         ·

        2022-02-09 17:29


        JAVA前線?


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



        1 三組概念

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

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

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

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

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

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

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

        2 簡單隨機(jī)

        簡單隨機(jī)含義是服務(wù)消費(fèi)者每次會任意訪問一個(gè)服務(wù)提供者,并且從概率角度看每個(gè)提供者被訪問概率一致,可以通過指定范圍隨機(jī)數(shù)實(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);
        }

        第三步編寫簡單隨機(jī)策略

        public?class?RandomBalance?extends?AbstractLoadBalance?{

        ????@Override
        ????public?MyServer?doSelect(List?serverList)?{
        ????????//?隨機(jī)數(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)隨機(jī)

        3.1 設(shè)計(jì)思路

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

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




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



        3.2 代碼實(shí)例

        第一步編寫服務(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)隨機(jī)策略

        public?class?RandomWeightBalance?extends?AbstractLoadBalance?{

        ????@Override
        ????public?MyServer?doSelect(List?serverList)?{
        ????????//?所有服務(wù)器總權(quán)重
        ????????int?totalWeight?=?0;
        ????????//?第一個(gè)服務(wù)器權(quán)重
        ????????int?firstWeight?=?serverList.get(0).getWeight();
        ????????//?所有服務(wù)器權(quán)重相等
        ????????boolean?sameWeight?=?true;
        ????????//?遍歷所有服務(wù)器
        ????????for?(MyServer?server?:?serverList)?{
        ????????????//?計(jì)算總權(quán)重
        ????????????totalWeight?+=?server.getWeight();
        ????????????//?任意一個(gè)invoker權(quán)重不等于第一個(gè)權(quán)重則設(shè)置sameWeight=false
        ????????????if?(sameWeight?&&?server.getWeight()?!=?firstWeight)?{
        ????????????????sameWeight?=?false;
        ????????????}
        ????????}
        ????????//?權(quán)重不相等則根據(jù)權(quán)重選擇
        ????????if?(!sameWeight)?{
        ????????????//?在總區(qū)間范圍[0,totalWeight)生成隨機(jī)數(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)重相等則隨機(jī)選擇
        ????????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)重?cái)?shù)組
        ????????int[]?weights?=?new?int[length];

        ????????//?第一個(gè)權(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;

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

        ????????????//?任意一個(gè)invoker權(quán)重不等于第一個(gè)權(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)重相等則隨機(jī)選擇
        ????????return?invokers.get(ThreadLocalRandom.current().nextInt(length));
        ????}
        }

        4 簡單輪詢

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

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

        ????@Override
        ????public?MyServer?doSelect(List?serverList)?{
        ????????//?atomicIndex自增大于服務(wù)器數(shù)量時(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ū)間這個(gè)思路,相較于加權(quán)隨機(jī)會有一些變化。



        第一步編寫簡單加權(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)?{
        ????????????//?自增方式計(jì)算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è)計(jì)思路

        簡單加權(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)輪詢策略,將請求比較均勻地分散至各個(gè)服務(wù)器,下圖說明了計(jì)算步驟:



        6.2 代碼實(shí)例

        第一步編寫服務(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);

        ????????//?更新時(shí)間
        ????????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)前時(shí)間
        ????????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;
        ????????????}
        ????????????//?計(jì)算總權(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 一致性哈希

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


        7.1 特性分析

        (1) 特性一

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

        ????????//?找到第一個(gè)大于客戶端哈希值的服務(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ā)生宕機(jī)只會影響client1路由結(jié)果,并不會影響其它客戶端路由結(jié)果:



        (3) 特性三

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



        7.2 代碼實(shí)例

        第一步編寫基礎(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)?{

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

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

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

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

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

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

        ????????//?計(jì)算提供者列表哈希值
        ????????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é)點(diǎn)參看以下代碼

        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é)點(diǎn)
        ????????????????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é)點(diǎn)(默認(rèn)160個(gè))
        ????????????????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();
        ????????}

        ????????//?哈希運(yùn)算
        ????????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ù)

        每個(gè)提供者維護(hù)并發(fā)處理的任務(wù)個(gè)數(shù),任務(wù)個(gè)數(shù)越大活躍度越高。在服務(wù)消費(fèi)者進(jìn)行負(fù)載均衡時(shí),第一查詢提供者負(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];

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

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

        ????????//?第一個(gè)調(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;

        ????????????//?第一個(gè)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;
        ????????????????}
        ????????????}
        ????????}

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

        ????????//?多個(gè)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);
        ????????????????}
        ????????????}
        ????????}
        ????????//?多個(gè)invoker調(diào)用次數(shù)等于最小調(diào)用次數(shù)并且權(quán)重值相同->隨機(jī)選擇
        ????????return?invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
        ????}
        }

        9 文章總結(jié)

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



        JAVA前線?


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


        瀏覽 61
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 午夜日b视频 | 胖女人毛片 | 中文字字幕中文字幕乱码 | 啊轻点灬太粗嗯别揉我 | 成年人视频在线 |