Spring Cloud Ribbon負載均衡策略詳解

這里使用的ribbon的版本是:ribbon-loadbalancer-2.2.2.jar。


一,IRule接口


IRule接口定義了選擇負載均衡策略的基本操作。通過調(diào)用choose()方法澳叉,就可以選擇具體的負載均衡策略。

// 選擇目標服務(wù)節(jié)點

Server choose(Object var1);

// 設(shè)置負載均衡策略

void setLoadBalancer(ILoadBalancer var1);

// 獲取負載均衡策略

ILoadBalancer getLoadBalancer();


二,ILoadBalancer接口


ILoadBalancer接口定義了ribbon負載均衡的常用操作成洗,有以下幾個方法:


void addServers(List<Server> var1);


Server chooseServer(Object var1);


void markServerDown(Server var1);


List<Server> getReachableServers();


List<Server> getAllServers();



三五督,AbstractLoadBalancerRule抽象類


AbstractLoadBalancerRule實現(xiàn)了IRule接口和IClientConfigAware接口,主要對IRule接口的2個方法進行了簡單封裝瓶殃。

private ILoadBalancer ib;

// 設(shè)置負載均衡策略

public void setLoadBalancer(ILoadBalancer ib){

? ? this.ib = ib;

}

// 獲取負載均衡策略

public ILoadBalancer getLoadBalancer(){

? ? return this.ib;

}


AbstractLoadBalancerRule是每個負載均衡策略需要直接繼承的類充包,Ribbon提供的幾個負載均衡策略,都繼承了這個抽象類遥椿。同理基矮,我們?nèi)绻枰远x負載均衡策略,也要繼承這個抽象類冠场。


四家浇,AbstractLoadBalancerRule的實現(xiàn)類


AbstractLoadBalancerRule的實現(xiàn)類就是ribbon的具體負載均衡策略,首先來看默認的輪詢策略碴裙。


1钢悲,輪詢策略(RoundRobinRule)

輪詢策略理解起來比較簡單,就是拿到所有的server集合青团,然后根據(jù)id進行遍歷譬巫。這里的id是ip+端口咖楣,Server實體類中定義的id屬性如下:

this.id = host + ":" + port

這里還有一點需要注意督笆,輪詢策略有一個上限,當(dāng)輪詢了10個服務(wù)端節(jié)點還沒有找到可用服務(wù)的話诱贿,輪詢結(jié)束娃肿。


2,隨機策略(RandomRule)

隨機策略:使用jdk自帶的隨機數(shù)生成工具珠十,生成一個隨機數(shù)料扰,然后去可用服務(wù)列表中拉取服務(wù)節(jié)點Server。如果當(dāng)前節(jié)點不可用焙蹭,則進入下一輪隨機策略晒杈,直到選到可用服務(wù)節(jié)點為止。

這里在while循環(huán)中孔厉,使用了Thread#interrupted()方法和Thread#yield()方法拯钻,使用的很巧妙,可以參考一下撰豺。

while(server == null) {

? ? if (Thread.interrupted()) {

? ? ? ? return null;

? ? }

……

? ? // 如果隨機打到的節(jié)點為null粪般,則再次循環(huán)隨機

? ? if (server == null) {

? ? ? ? Thread.yield();

? ? } else {

? ? ? ? if (server.isAlive()) {

? ? ? ? ? ? return server;

? ? ? ? }

? ? ? ? // 如果節(jié)點不是存活狀態(tài),則再次循環(huán)隨機

? ? ? ? server = null;

? ? ? ? Thread.yield();

? ? }

}


3污桦,可用過濾策略(AvailabilityFilteringRule)

策略描述:過濾掉連接失敗的服務(wù)節(jié)點亩歹,并且過濾掉高并發(fā)的服務(wù)節(jié)點,然后從健康的服務(wù)節(jié)點中,使用輪詢策略選出一個節(jié)點返回小作。


AvailabilityFilteringRule#choose()方法實現(xiàn)如下:

// 記錄輪詢次數(shù)亭姥,最多輪詢10次

int count = 0;

// 輪詢10次

for(Server server = roundRobinRule.choose(key); count++ <= 10; server = roundRobinRule.choose(key)) {

? ? if (this.predicate.apply(new PredicateKey(server))) {

? ? ? ? return server;

? ? }

}

// 如果輪詢10次還沒有找到可用server,則執(zhí)行父類中的篩選邏輯

return super.choose(key);


4顾稀,響應(yīng)時間權(quán)重策略

(WeightedResponseTimeRule)

策略描述:根據(jù)響應(yīng)時間致份,分配一個權(quán)重weight,響應(yīng)時間越長础拨,weight越小氮块,被選中的可能性越低。

如何計算權(quán)重呢诡宗?代碼邏輯位于WeightedResponseTimeRule$$ServerWeight#maintainWeights()滔蝉,判斷邏輯還是挺復(fù)雜的,暫時沒時間看塔沃,先留著蝠引,以后有機會再研究。

注意蛀柴,服務(wù)剛啟動時螃概,由于統(tǒng)計信息不足,先使用輪詢策略鸽疾。等到信息足夠了吊洼,切換到WeightedResponseTimeRule策略。


5制肮,輪詢失敗重試策略(RetryRule)


輪詢失敗重試策略(RetryRule)是這樣工作的冒窍,首先使用輪詢策略進行負載均衡,如果輪詢失敗豺鼻,則再使用輪詢策略進行一次重試综液,相當(dāng)于重試下一個節(jié)點,看下一個節(jié)點是否可用儒飒,如果再失敗谬莹,則直接返回失敗。

這里還有一個點要注意桩了,重試的時間間隔附帽,默認是500毫秒,我們可以自定義這個重試時間間隔圣猎。

this.maxRetryMillis = 500L;


另外士葫,失敗重試策略的源碼中,RetryRule#choose()方法很有參考價值送悔,如果我們需要自己實現(xiàn)調(diào)用接口的失敗重試功能的話慢显,可以參考這個方法爪模。這個方法中給我們展示了如何使用Thread#interrupted()和Thread#yield()。


6荚藻,并發(fā)量最小可用策略(BestAvailableRule)

策略描述:選擇一個并發(fā)量最小的server返回屋灌。如何判斷并發(fā)量最小呢?ServerStats有個屬性activeRequestCount应狱,這個屬性記錄的就是server的并發(fā)量共郭。輪詢所有的server,選擇其中activeRequestCount最小的那個server疾呻,就是并發(fā)量最小的服務(wù)節(jié)點除嘹。

接下來我們看源碼:

if (this.loadBalancerStats == null) {

? ? return super.choose(key);

} else {

? ? // 獲取所有的server

? ? List<Server> serverList = this.getLoadBalancer().getAllServers();

? ? int minimalConcurrentConnections = 2147483647;

? ? long currentTime = System.currentTimeMillis();

? ? Server chosen = null;

? ? Iterator var7 = serverList.iterator();

? ?

? ? while(var7.hasNext()) {

? ? ? ? Server server = (Server)var7.next();

? ? ? ? ServerStats serverStats = this.loadBalancerStats.getSingleServerStats(server);

? ? ? ? // 判斷斷路器是否跳閘,如果沒有跳閘岸蜗,繼續(xù)往下走尉咕。

? ? ? ? if (!serverStats.isCircuitBreakerTripped(currentTime)) {

? ? ? ? ? ? int concurrentConnections = serverStats.getActiveRequestCount(currentTime);

? ? ? ? ? ? if (concurrentConnections < minimalConcurrentConnections) {

? ? ? ? ? ? ? ? minimalConcurrentConnections = concurrentConnections;

? ? ? ? ? ? ? ? chosen = server;

? ? ? ? ? ? }

? ? ? ? }

? ? }


? ? // 如果沒有找到并發(fā)量最小的服務(wù)節(jié)點,則使用父類的策略

? ? if (chosen == null) {

? ? ? ? return super.choose(key);

? ? } else {

? ? ? return chosen;

? ? }

}


并發(fā)量最小可用策略(BestAvailableRule)的優(yōu)點是:可以充分考慮每臺服務(wù)節(jié)點的負載璃岳,把請求打到負載壓力最小的服務(wù)節(jié)點上年缎。但是缺點是:因為需要輪詢所有的服務(wù)節(jié)點,如果集群數(shù)量太大铃慷,那么就會比較耗時单芜。當(dāng)然一般來說,幾十臺犁柜,幾百臺的集群數(shù)量是不用考慮這個問題的洲鸠。因此對于大部分的項目而言,是一個不錯的選擇赁温。


7坛怪,ZoneAvoidanceRule

策略描述:復(fù)合判斷server所在區(qū)域的性能和server的可用性淤齐,來選擇server返回股囊。


四,BaseLoadBalancer


BaseLoadBalancer是一個負載均衡器更啄,是ribbon框架提供的負載均衡器稚疹。Spring Cloud對ribbon封裝以后,直接調(diào)用ribbon的負載均衡器來實現(xiàn)微服務(wù)客戶端的負載均衡祭务。

這里需要注意内狗,ribbon框架本身提供了幾個負載均衡器,BaseLoadBalancer只是其中之一义锥。

Spring Cloud是如何封裝ribbon框架的呢柳沙?Spring Cloud提供了2個接口:ServiceInstanceChooser和LoadBalancerClient,這2個接口就是客戶端負載均衡的定義拌倍。具體實現(xiàn)類是RibbonLoadBalancerClient赂鲤。RibbonLoadBalancerClient#choose()方法根據(jù)微服務(wù)實例的serviceId噪径,然后使用配置的負載均衡策略,打到對于的微服務(wù)實例節(jié)點上数初。

OK找爱,到這里,我們簡單梳理一下Spring Cloud集成ribbon后泡孩,負載均衡的執(zhí)行邏輯车摄。

1,Spring Cloud RibbonLoadBalancerClient#choose()調(diào)用ribbon框架的BaseLoadBalancer仑鸥。

2吮播,BaseLoadBalancer#chooseServer()選擇具體的負載均衡策略(RoundRibonRule),然后執(zhí)行眼俊。

但是薄料,RibbonLoadBalancerClient#choose()是在哪里調(diào)用的呢?這里用到了攔截器泵琳,@RibbonClient注解自動化配置類LoadBalancerAutoConfiguration.class中有兩個注解:

@ConditionalOnClass({RestTemplate.class})

@ConditionalOnClass({LoadBalancerClient.class})

也就是說摄职,在RestTemplate.class和LoadBalancerClient.class存在的情況下,LoadBalancerInterceptor.class會攔截RestTemplate.class上的@LoadBalanced注解获列,然后將請求中的微服務(wù)實例名serviceId轉(zhuǎn)化為具體的ip+端口谷市,然后去請求目標服務(wù)節(jié)點。


OK击孩,有點亂迫悠,我們再來梳理一下調(diào)用關(guān)系:

1,@LoadBalanced注解

2巩梢,org.springframework.web.client.RestTemplate

3创泄,LoadBalancerAutoConfiguration.class

4,LoadBalancerInterceptor.class攔截org.springframework.web.client.RestTemplate的請求括蝠,注入客戶端負載均衡功能鞠抑,發(fā)送請求到目標服務(wù)節(jié)點。

這就是Spring Cloud 集成的ribbon客戶端負載均衡忌警。


五搁拙,如何自定義負載均衡策略


Ribbon不僅實現(xiàn)了幾種負載均衡策略,也為開發(fā)者提供了自定義負載均衡策略的支持法绵。

自定義負載均衡策略有3個關(guān)鍵點:

1箕速,繼承抽象類AbstractLoadBalancerRule

2,自定義的負載均衡策略類朋譬,不能放在@ComponentScan所掃描的當(dāng)前包和子包下盐茎。

3,在主啟動類上添加@RibbonClient注解徙赢,或者在配置文件中指定哪個微服務(wù)使用自定義負載均衡策略字柠。

@RibbonClient注解的使用方法如下:

@RibbonClient(name="微服務(wù)名稱", configuration="自定義配置類.class")

配置文件中配置項如下:

springboot.微服務(wù)名稱.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule

或者

springboot.微服務(wù)名稱.NFLoadBalancerRuleClassName=com.xxx.xxx.xxx.自定義負載均衡策略實現(xiàn)類


這里需要注意滑进,Spring Cloud微服務(wù)啟動類上有個注解@SpringBootApplication,這個注解的源碼上標注了@ScanComponent注解募谎,也就是說扶关,我們的微服務(wù)啟動類其實間接引入了@ComponentScan注解。

@ComponentScan注解的作用就是掃描當(dāng)前包及其子包下的標有指定注解的bean数冬,然后把它們注入到IOC容器节槐。

@Component會掃描哪些注解呢?有4個注解拐纱,分別是:

@Component

@Service

@Controller

@Repository

另外铜异,所有間接引入了上面4個注解的注解,最終也會被@ComponentScan掃描秸架,比如我們常用的@Configuration揍庄,也會被@ComponentScan掃描。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末东抹,一起剝皮案震驚了整個濱河市蚂子,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缭黔,老刑警劉巖食茎,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異馏谨,居然都是意外死亡别渔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進店門惧互,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哎媚,“玉大人,你說我怎么就攤上這事喊儡〔τ耄” “怎么了?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵管宵,是天一觀的道長截珍。 經(jīng)常有香客問我,道長箩朴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任秋度,我火速辦了婚禮炸庞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘荚斯。我一直安慰自己埠居,他們只是感情好查牌,可當(dāng)我...
    茶點故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著滥壕,像睡著了一般纸颜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上负蚊,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天沪停,我揣著相機與錄音渴庆,去河邊找鬼。 笑死涮较,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的冈止。 我是一名探鬼主播狂票,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼熙暴!你這毒婦竟也來了闺属?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤周霉,失蹤者是張志新(化名)和其女友劉穎屋剑,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诗眨,經(jīng)...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡唉匾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了匠楚。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巍膘。...
    茶點故事閱讀 38,768評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖芋簿,靈堂內(nèi)的尸體忽然破棺而出峡懈,到底是詐尸還是另有隱情,我是刑警寧澤与斤,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布肪康,位于F島的核電站,受9級特大地震影響撩穿,放射性物質(zhì)發(fā)生泄漏磷支。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一食寡、第九天 我趴在偏房一處隱蔽的房頂上張望雾狈。 院中可真熱鬧,春花似錦抵皱、人聲如沸善榛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽移盆。三九已至悼院,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咒循,已是汗流浹背据途。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留剑鞍,地道東北人昨凡。 一個月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像蚁署,于是被迫代替她去往敵國和親便脊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,666評論 2 350

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