Spring Cloud入門教程(二):客戶端負(fù)載均衡(Ribbon)

上一篇:《Spring Cloud入門教程(一):服務(wù)治理(Eureka)》

本人和同事撰寫的《Spring Cloud微服務(wù)架構(gòu)開發(fā)實(shí)戰(zhàn)》一書也在京東派敷、當(dāng)當(dāng)?shù)葧晟霞埽蠹铱梢渣c(diǎn)擊這里前往購買,多謝大家支持和捧場篮愉!


對(duì)于大型應(yīng)用系統(tǒng)負(fù)載均衡(LB:Load Balancing)是首要被解決一個(gè)問題腐芍。在微服務(wù)之前LB方案主要是集中式負(fù)載均衡方案,在服務(wù)消費(fèi)者和服務(wù)提供者之間又一個(gè)獨(dú)立的LB试躏,LB通常是專門的硬件猪勇,如F5,或者是基于軟件的颠蕴,如VS泣刹、HAproxy等。LB上有所有服務(wù)的地址映射表犀被,當(dāng)服務(wù)消費(fèi)者調(diào)用某個(gè)目標(biāo)服務(wù)時(shí)椅您,它先向LB發(fā)起請(qǐng)求,由LB以某種策略(比如:Round-Robin)做負(fù)載均衡后將請(qǐng)求轉(zhuǎn)發(fā)到目標(biāo)服務(wù)寡键。

而微服務(wù)的出現(xiàn)掀泳,則為LB的實(shí)現(xiàn)提供了另外一種思路:把LB的功能以庫的方式集成到服務(wù)消費(fèi)方的進(jìn)程內(nèi),而不是由一個(gè)集中的設(shè)備或服務(wù)器提供西轩。這種方案稱為軟負(fù)載均衡(Soft Load Balancing)或者客戶端負(fù)載均衡开伏。在Spring Cloud中配合Eureka的服務(wù)注冊(cè)功能,Ribbon子項(xiàng)目則為REST客戶端實(shí)現(xiàn)了負(fù)載均衡遭商。

接下來我們將通過擴(kuò)展上一篇中的示例代碼來搭建一個(gè)基于Ribbon的客戶端負(fù)載均衡示例固灵。

1. 示例代碼

保持上一篇中的代碼不變。

2. 如何啟用Ribbon

在上一篇中的consumer-hello工程(也就是我們的服務(wù)消費(fèi)方)的pom的依賴中聲明了:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>   

也就是說在本項(xiàng)目中將會(huì)啟動(dòng)Ribbon劫流。

2.1 啟動(dòng)類

@EnableDiscoveryClient
@SpringBootApplication
public class Application {

    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

在該啟動(dòng)類中:

  • @EnableDiscoveryClient注解巫玻,說明這是一個(gè)Eureka客戶端,同時(shí)也可以獲取到Eureka服務(wù)器中所注冊(cè)的服務(wù)列表;
  • RestTemplate中我們?cè)黾恿艘粋€(gè)@LoadBalanced注解祠汇,該注解就是能夠讓RestTemplate啟用客戶端負(fù)載均衡仍秤。

備注:如果你對(duì)Spring的RestTemplate使用還不是很了解,可以參考我的這篇文章《Spring Cloud入門教程(番外篇一):RestTemplate使用指南》可很。

3. 負(fù)載均衡測試

1) 啟動(dòng)Eureka服務(wù)器;
2) 啟動(dòng)多個(gè)服務(wù)提供者:SERVICE-HELLO诗力,這里我啟動(dòng)了兩個(gè):

java -jar service-hello-1.0.0-SNAPSHOT.jar

java -jar service-hello-1.0.0-SNAPSHOT.jar --server.port=2200

通過Maven將Service-Hello打包成一個(gè)FatJar,可以使用上面的命令啟動(dòng)多個(gè)實(shí)例我抠。這里SERVICE-HELLO的兩個(gè)實(shí)例服務(wù)器端口分別為:21002200苇本。

3) 啟動(dòng)服務(wù)消費(fèi)者:consumer-hello;
4) 打開Eureka服務(wù)器管理頁面:http://localhost:8260,可以看到已經(jīng)有兩個(gè)SERVICE-HELLO服務(wù)和一個(gè)CONSUMER-HELLO服務(wù)注冊(cè)進(jìn)來了菜拓。

Ribbon - Eureka Server

5) 打開Consumer-Hello:http://localhost:8080/hello瓣窄,多次刷新,在頁面中我們可以看到下面兩個(gè)輸出交替出現(xiàn):

Ribbon - Eureka Server

Ribbon - Eureka Server

說明了consumer-hello在交替訪問SERVICE-HELLO的兩個(gè)服務(wù)實(shí)例纳鼎,也就是說我們的負(fù)載均衡測試是成功的俺夕。

4. 直接使用Ribbon的API

另外裳凸,除了使用@LoadBalanced注解外,我們還可以直接使用Ribbon所提供的LoadBalancerClient來實(shí)現(xiàn)負(fù)載均衡:

@RestController
public class HelloController {
    protected Logger logger = LoggerFactory.getLogger(HelloController.class);

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello() {
        return restTemplate.getForEntity("http://SERVICE-HELLO/hello", String.class).getBody();
    }

    @RequestMapping(value = "/helloEx", method = RequestMethod.GET)
    public String helloEx() {
        ServiceInstance instance = this.loadBalancerClient.choose("SERVICE-HELLO");
        URI helloUri = URI.create(String.format("http://%s:%s/hello", instance.getHost(), instance.getPort()));        
        logger.info("Target service uri = {}. ", helloUri.toString());
        return new RestTemplate().getForEntity(helloUri, String.class).getBody();
    }
}

同樣多次訪問http://localhost:8080/helloEx后劝贸,在控制臺(tái)中將會(huì)輸出以下日志:

2017-06-26 21:31:30 891 [http-nio-8080-exec-3] INFO  i.t.c.c.h.controller.HelloController - Target service uri = http://cd826dembp.lan:2100/hello. 
2017-06-26 21:31:33 008 [http-nio-8080-exec-4] INFO  i.t.c.c.h.controller.HelloController - Target service uri = http://cd826dembp.lan:2200/hello. 
2017-06-26 21:31:34 447 [http-nio-8080-exec-6] INFO  i.t.c.c.h.controller.HelloController - Target service uri = http://cd826dembp.lan:2100/hello. 
2017-06-26 21:31:35 395 [http-nio-8080-exec-7] INFO  i.t.c.c.h.controller.HelloController - Target service uri = http://cd826dembp.lan:2200/hello. 
2017-06-26 21:31:36 220 [http-nio-8080-exec-8] INFO  i.t.c.c.h.controller.HelloController - Target service uri = http://cd826dembp.lan:2100/hello. 

說明負(fù)載均衡是一樣啟作用的姨谷。

注意:helloEx方法中不能夠使用之前自動(dòng)織入的restTemplate,否則會(huì)報(bào)以下錯(cuò)誤:

java.lang.IllegalStateException: No instances available for cd826dembp.lan

這個(gè)是因?yàn)樽詣?dòng)織入的restTemplate會(huì)把服務(wù)器的邏輯名稱當(dāng)作服務(wù)名稱來使用映九,造成找不到相應(yīng)的服務(wù)實(shí)例梦湘,從而報(bào)錯(cuò)。

另外氯迂,我們還可以采用下面的方式:

@Primary
@Bean(name="lbcRestTemplate")
RestTemplate lbcRestTemplate() {
    return new RestTemplate();
}

上面的類更改為:

@RestController
public class HelloController {
    protected Logger logger = LoggerFactory.getLogger(HelloController.class);

    @Autowired
    @Qualifier(value = "restTemplate")
    private RestTemplate restTemplate;

    @Autowired
    @Qualifier(value = "lbcRestTemplate")
    private RestTemplate lbcRestTemplate;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello() {
        return restTemplate.getForEntity("http://SERVICE-HELLO/hello", String.class).getBody();
    }

    @RequestMapping(value = "/helloEx", method = RequestMethod.GET)
    public String helloEx() {
        ServiceInstance instance = this.loadBalancerClient.choose("SERVICE-HELLO");
        URI helloUri = URI.create(String.format("http://%s:%s/hello", instance.getHost(), instance.getPort()));
        logger.info("Target service uri = {}. ", helloUri.toString());
        return this.lbcRestTemplate.getForEntity(helloUri, String.class).getBody();
    }
}

5. Ribbon負(fù)載均衡策略

Ribbon本身提供了下面幾種負(fù)載均衡策略:

  • RoundRobinRule: 輪詢策略践叠,Ribbon以輪詢的方式選擇服務(wù)器言缤,這個(gè)是默認(rèn)值嚼蚀。所以示例中所啟動(dòng)的兩個(gè)服務(wù)會(huì)被循環(huán)訪問;
  • RandomRule: 隨機(jī)選擇,也就是說Ribbon會(huì)隨機(jī)從服務(wù)器列表中選擇一個(gè)進(jìn)行訪問;
  • BestAvailableRule: 最大可用策略管挟,即先過濾出故障服務(wù)器后轿曙,選擇一個(gè)當(dāng)前并發(fā)請(qǐng)求數(shù)最小的;
  • WeightedResponseTimeRule: 帶有加權(quán)的輪詢策略,對(duì)各個(gè)服務(wù)器響應(yīng)時(shí)間進(jìn)行加權(quán)處理僻孝,然后在采用輪詢的方式來獲取相應(yīng)的服務(wù)器;
  • AvailabilityFilteringRule: 可用過濾策略导帝,先過濾出故障的或并發(fā)請(qǐng)求大于閾值一部分服務(wù)實(shí)例,然后再以線性輪詢的方式從過濾后的實(shí)例清單中選出一個(gè);
  • ZoneAvoidanceRule: 區(qū)域感知策略穿铆,先使用主過濾條件(區(qū)域負(fù)載器您单,選擇最優(yōu)區(qū)域)對(duì)所有實(shí)例過濾并返回過濾后的實(shí)例清單,依次使用次過濾條件列表中的過濾條件對(duì)主過濾條件的結(jié)果進(jìn)行過濾荞雏,判斷最小過濾數(shù)(默認(rèn)1)和最小過濾百分比(默認(rèn)0)虐秦,最后對(duì)滿足條件的服務(wù)器則使用RoundRobinRule(輪詢方式)選擇一個(gè)服務(wù)器實(shí)例。

我們可以通過繼承ClientConfigEnabledRoundRobinRule凤优,來實(shí)現(xiàn)自己負(fù)載均衡策略悦陋。

這里,大家先對(duì)Ribbon提供的負(fù)載均衡有一個(gè)概念筑辨,后續(xù)我們?cè)僭敿?xì)分析各個(gè)負(fù)載均衡策略的具體處理邏輯及配置方式俺驶。

6. 小結(jié)

Ribbon的主要功能是為REST客戶端實(shí)現(xiàn)負(fù)載均衡,其工作原理可以概括為下面四個(gè)步驟:

  1. Ribbon首先根據(jù)其所在Zone優(yōu)先選擇一個(gè)負(fù)載較少的Eureka Server;
  2. 定期從Eureka Server更新并過濾服務(wù)實(shí)例列表;
  3. 根據(jù)指定的負(fù)載均衡策略棍辕,從可用的服務(wù)器列表中選擇一個(gè)服務(wù)實(shí)例的地址;
  4. 然后通過RestClient進(jìn)行服務(wù)調(diào)用暮现。

這時(shí)我們系統(tǒng)的架構(gòu)變成為:

Ribbon - 040

你可以到這里下載本篇的代碼。

下一篇:《Spring Cloud入門教程(三):聲明式服務(wù)調(diào)用(Feign)》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末楚昭,一起剝皮案震驚了整個(gè)濱河市送矩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哪替,老刑警劉巖栋荸,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡晌块,警方通過查閱死者的電腦和手機(jī)爱沟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來匆背,“玉大人呼伸,你說我怎么就攤上這事《凼” “怎么了括享?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長珍促。 經(jīng)常有香客問我铃辖,道長,這世上最難降的妖魔是什么猪叙? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任娇斩,我火速辦了婚禮,結(jié)果婚禮上穴翩,老公的妹妹穿的比我還像新娘犬第。我一直安慰自己,他們只是感情好芒帕,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布歉嗓。 她就那樣靜靜地躺著,像睡著了一般背蟆。 火紅的嫁衣襯著肌膚如雪鉴分。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天淆储,我揣著相機(jī)與錄音冠场,去河邊找鬼译荞。 笑死茫藏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的陶缺。 我是一名探鬼主播点额,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼舔株,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了还棱?” 一聲冷哼從身側(cè)響起载慈,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎珍手,沒想到半個(gè)月后办铡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辞做,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年寡具,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秤茅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡童叠,死狀恐怖框喳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情厦坛,我是刑警寧澤五垮,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站杜秸,受9級(jí)特大地震影響放仗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜亩歹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一匙监、第九天 我趴在偏房一處隱蔽的房頂上張望凡橱。 院中可真熱鬧小作,春花似錦、人聲如沸稼钩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坝撑。三九已至静秆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間巡李,已是汗流浹背抚笔。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侨拦,地道東北人殊橙。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像狱从,于是被迫代替她去往敵國和親膨蛮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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