spring cloud ribbon學(xué)習(xí)四:負(fù)載均衡策略

本篇博客只是分析一下基本的服裝均衡策略驻呐,com.netflix.loadbalancer.IRule接口的一些實(shí)現(xiàn)逛裤。

負(fù)載均衡策略

負(fù)載均衡接口com.netflix.loadbalancer.IRule

IRule繼承圖

com.netflix.loadbalancer.AbstractLoadBalancerRule

負(fù)載均衡策略的抽象類欢摄,在該抽象類中定義了負(fù)載均衡器ILoadBalancer對象了赌,該對象能夠在具體實(shí)現(xiàn)選擇服務(wù)策略時(shí)葛峻,獲取到一些負(fù)載均衡器中一些維護(hù)的信息來作為分配的依據(jù)两波,并以此設(shè)計(jì)一些算法來實(shí)現(xiàn)針對特定場景的高效率策略。

public abstract class AbstractLoadBalancerRule implements IRule, IClientConfigAware {

    private ILoadBalancer lb;
        
    @Override
    public void setLoadBalancer(ILoadBalancer lb){
        this.lb = lb;
    }
    
    @Override
    public ILoadBalancer getLoadBalancer(){
        return lb;
    }      
}

com.netflix.loadbalancer.RandomRule

該策略實(shí)現(xiàn)了從服務(wù)清單中隨機(jī)選擇一個(gè)服務(wù)實(shí)例的功能击儡。

節(jié)選了部分源碼塔沃,也是通過負(fù)載均衡器:

RandomRule算法

com.netflix.loadbalancer.RoundRobinRule

該策略實(shí)現(xiàn)按照線性輪詢的方式依次選擇實(shí)例的功能。它的具體實(shí)現(xiàn)如下阳谍,在循環(huán)中增加了一個(gè)count計(jì)數(shù)變量芳悲,該變量會(huì)在每次輪詢之后累加,如果輪詢次數(shù)Server超過10次边坤,選擇不到實(shí)例的話名扛,會(huì)報(bào)警告信息。

RoundRobinRule算法邏輯

com.netflix.loadbalancer.RetryRule

該策略實(shí)現(xiàn)了一個(gè)具備重試機(jī)制的實(shí)例選擇功能茧痒。具有RoundRobinRule實(shí)例的一個(gè)引用肮韧。也就是在一段時(shí)間內(nèi)通過RoundRobinRule選擇服務(wù)實(shí)例,一段時(shí)間內(nèi)沒有選擇出服務(wù)則線程終止旺订。

RetryRule的算法

com.netflix.loadbalancer.WeightedResponseTimeRule

該策略是對RoundRobinRule的擴(kuò)展弄企,增加了根據(jù)實(shí)例的運(yùn)行情況來計(jì)算權(quán)重,并根據(jù)權(quán)重來挑選實(shí)例区拳,以達(dá)到更優(yōu)的分配效果拘领,增加了三個(gè)核心的內(nèi)容:

會(huì)在啟動(dòng)的時(shí)候啟動(dòng)一個(gè)定時(shí)任務(wù),去計(jì)算每個(gè)實(shí)例的權(quán)重樱调,默認(rèn)30s執(zhí)行一次

void initialize(ILoadBalancer lb) {        
      if (serverWeightTimer != null) {
          serverWeightTimer.cancel();
      }
      serverWeightTimer = new Timer("NFLoadBalancer-serverWeightTimer-"
              + name, true);
      serverWeightTimer.schedule(new DynamicServerWeightTask(), 0,
              serverWeightTaskTimerInterval);
      // do a initial run
      ServerWeight sw = new ServerWeight();
      sw.maintainWeights();

      Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
          public void run() {
              logger
                      .info("Stopping NFLoadBalancer-serverWeightTimer-"
                              + name);
              serverWeightTimer.cancel();
          }
   }));
}

具體計(jì)算權(quán)重的算法

class DynamicServerWeightTask extends TimerTask {
      public void run() {
          ServerWeight serverWeight = new ServerWeight();
          try {
           //具體計(jì)算權(quán)重的方法
             serverWeight.maintainWeights();
            }catch (Throwable t) {
              logger.error(
                      "Throwable caught while running DynamicServerWeightTask for "
                              + name, t);
            }
        }
}

權(quán)重算法

      public void maintainWeights() {
            ILoadBalancer lb = getLoadBalancer();
            if (lb == null) {
                return;
            }
            if (serverWeightAssignmentInProgress.get()) {
                return; // Ping in progress - nothing to do
            } else {
                serverWeightAssignmentInProgress.set(true);
            }
            try {
                logger.info("Weight adjusting job started");
                AbstractLoadBalancer nlb = (AbstractLoadBalancer) lb;
                LoadBalancerStats stats = nlb.getLoadBalancerStats();
                if (stats == null) {
                    // no statistics, nothing to do
                    return;
                }
                double totalResponseTime = 0;
                // find maximal 95% response time
                for (Server server : nlb.getAllServers()) {
                    // this will automatically load the stats if not in cache
                    ServerStats ss = stats.getSingleServerStat(server);
                    totalResponseTime += ss.getResponseTimeAvg();
                }
                // weight for each server is (sum of responseTime of all servers - responseTime)
                // so that the longer the response time, the less the weight and the less likely to be chosen
                Double weightSoFar = 0.0;
                
                // create new list and hot swap the reference
                List<Double> finalWeights = new ArrayList<Double>();
                for (Server server : nlb.getAllServers()) {
                    ServerStats ss = stats.getSingleServerStat(server);
                    double weight = totalResponseTime - ss.getResponseTimeAvg();
                    weightSoFar += weight;
                    finalWeights.add(weightSoFar);   
                }
                setWeights(finalWeights);
            } catch (Throwable t) {
                logger.error("Exception while dynamically calculating server weights", t);
            } finally {
                serverWeightAssignmentInProgress.set(false);
            }

        }
    }

舉個(gè)簡單的列子约素,就是4個(gè)實(shí)例,A笆凌,B圣猎,C,D平均響應(yīng)時(shí)間為10乞而,40送悔,80,100,所以總響應(yīng)時(shí)間是10+40+80+100=230欠啤,每個(gè)實(shí)例權(quán)重為總響應(yīng)時(shí)間與實(shí)際自身的平均響應(yīng)時(shí)間的差的累積所得荚藻,所以A,B,C,D的權(quán)重分別如下:
實(shí)例A: 230-10=220
實(shí)例B:220+(230-40)=410
實(shí)例C:410+(230-80)=560
實(shí)例D:560+(230-100)=690

所以實(shí)例A:[0.220]
實(shí)例B:(220,410]
實(shí)例C:(410,560]
實(shí)例D:(560,690)

com.netflix.loadbalancer.ClientConfigEnabledRoundRobinRule

該策略較為特殊洁段,一般不直接使用应狱,本身沒有實(shí)現(xiàn)什么特殊的邏輯,內(nèi)部也定義了一個(gè)RoundRobinRule策略眉撵,而choose函數(shù)的實(shí)現(xiàn)也正是使用了RoundRobinRule的線性輪詢機(jī)制侦香,所以它實(shí)現(xiàn)的功能實(shí)際上與RoundRobinRule相同落塑。在子類中做一些高級策略時(shí)通常有可能會(huì)存在一些無法實(shí)施的情況纽疟,使用父類的實(shí)現(xiàn)作為備選。

ClientConfigEnabledRoundRobinRule

com.netflix.loadbalancer.BestAvailableRule

該策略繼承ClientConfigEnabledRoundRobinRule憾赁,在實(shí)現(xiàn)中它注入了負(fù)載均衡的統(tǒng)計(jì)對象LoadBalancerStats污朽,同時(shí)在具體的choose算法中利用LoadBalancerStats保存的實(shí)例統(tǒng)計(jì)信息來選擇滿足要求的實(shí)例。它通過遍歷負(fù)載均衡器中的維護(hù)的所有實(shí)例龙考,會(huì)過濾掉故障的實(shí)例蟆肆,并找出并發(fā)請求數(shù)最小的一個(gè),所以該策略的特性時(shí)可選出最空閑的實(shí)例晦款。

同時(shí)炎功,該算法核心依賴與LoadBalancerStats,當(dāng)其為空時(shí)候缓溅,策略是無法執(zhí)行蛇损,執(zhí)行父類的線性輪詢機(jī)制。

BestAvailableRule的算法

com.netflix.loadbalancer.PredicateBasedRule

這是個(gè)抽象策略坛怪,他也繼承了ClientConfigEnabledRoundRobinRule淤齐,這是一個(gè)基于Predicate實(shí)現(xiàn)的策略,PredicateGoogle Guava Collection工具對集合進(jìn)行過濾的條件接口(java8也引入了這個(gè)概念)

PredicateBasedRule的源碼
chooseRoundRobinAfterFiltering方法

com.netflix.loadbalancer.AvailabilityFilteringRule

繼承PredicateBasedRule類袜匿,過濾邏輯是AvailabilityPredicate類更啄。

AvailabilityFilteringRule代碼

AvailabilityPredicate的過濾邏輯:


AvailabilityPredicate

AvailabilityFilteringRule的輪詢算法:

AvailabilityFilteringRule的輪詢算法

com.netflix.loadbalancer.ZoneAvoidanceRule

該策略是com.netflix.loadbalancer.PredicateBasedRule的具體實(shí)現(xiàn)類。它使用了CompositePredicate來進(jìn)行服務(wù)實(shí)例清單的過濾居灯。這是一個(gè)組合過濾條件祭务,在其構(gòu)造函數(shù)中,它以ZoneAvoidancePredicate為主要過濾條件怪嫌,AvailabilityPredicate為次要過濾條件初始化了組合過濾條件的實(shí)例待牵。

ZoneAvoidanceRule源碼

ZoneAvoidanceRule并沒有重寫choose方法,完全遵循了父類的過濾主邏輯:“先過濾清單喇勋,再輪詢選擇”缨该。

public class CompositePredicate extends AbstractServerPredicate {

    private AbstractServerPredicate delegate;
    
    private List<AbstractServerPredicate> fallbacks = Lists.newArrayList();
    
    @Override
    public List<Server> getEligibleServers(List<Server> servers, Object loadBalancerKey) {
        List<Server> result = super.getEligibleServers(servers, loadBalancerKey);
        Iterator<AbstractServerPredicate> i = fallbacks.iterator();
        while (!(result.size() >= minimalFilteredServers && result.size() > (int) (servers.size() * minimalFilteredPercentage))
                && i.hasNext()) {
            AbstractServerPredicate predicate = i.next();
            result = predicate.getEligibleServers(servers, loadBalancerKey);
        }
        return result;
        
    }
}

在獲取過濾結(jié)果的實(shí)現(xiàn)函數(shù)getEligibleServers中,處理邏輯如下:

  • 使用主過濾條件對所有實(shí)例過濾并返回過濾后的實(shí)例清單川背。
  • 依次使用過濾條件進(jìn)行過濾
  • 每次過濾后都去判斷二個(gè)條件:過濾后的實(shí)例總數(shù) >=最小過濾實(shí)例數(shù)(minimalFilteredServers贰拿,默認(rèn)值是1)蛤袒,過濾后的實(shí)例比例 > 最小過濾百分比(minimalFilteredPercentage,默認(rèn)值為0)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末膨更,一起剝皮案震驚了整個(gè)濱河市妙真,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荚守,老刑警劉巖珍德,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異矗漾,居然都是意外死亡锈候,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進(jìn)店門敞贡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泵琳,“玉大人,你說我怎么就攤上這事誊役』窳校” “怎么了?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵蛔垢,是天一觀的道長击孩。 經(jīng)常有香客問我,道長鹏漆,這世上最難降的妖魔是什么巩梢? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮甫男,結(jié)果婚禮上且改,老公的妹妹穿的比我還像新娘。我一直安慰自己板驳,他們只是感情好又跛,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著若治,像睡著了一般慨蓝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上端幼,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天礼烈,我揣著相機(jī)與錄音,去河邊找鬼婆跑。 笑死此熬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播犀忱,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼募谎,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了阴汇?” 一聲冷哼從身側(cè)響起数冬,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎搀庶,沒想到半個(gè)月后拐纱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡哥倔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年秸架,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片未斑。...
    茶點(diǎn)故事閱讀 38,768評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡咕宿,死狀恐怖币绩,靈堂內(nèi)的尸體忽然破棺而出蜡秽,到底是詐尸還是另有隱情,我是刑警寧澤缆镣,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布芽突,位于F島的核電站,受9級特大地震影響董瞻,放射性物質(zhì)發(fā)生泄漏寞蚌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一钠糊、第九天 我趴在偏房一處隱蔽的房頂上張望挟秤。 院中可真熱鬧,春花似錦抄伍、人聲如沸艘刚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽攀甚。三九已至,卻和暖如春岗喉,著一層夾襖步出監(jiān)牢的瞬間秋度,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工钱床, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留荚斯,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像事期,于是被迫代替她去往敵國和親拐格。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評論 2 350

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