思考一個(gè)問題:如果為同一個(gè)提供者在Eureka中注冊(cè)多個(gè)服務(wù)?客戶端應(yīng)該如何選擇服務(wù)呢?

這時(shí),就需要在客戶端實(shí)現(xiàn)服務(wù)的負(fù)載均衡。而在Spring Cloud中推薦使用Ribbon來實(shí)現(xiàn)負(fù)載均衡。

 
1、Ribbon簡(jiǎn)介

Ribbon是Netflix發(fā)布的負(fù)載均衡器。它有助于控制HTTP和TCP客戶端的行為。為Ribbon配置服務(wù)提供者地址列表后, Ribbon就可基于某種負(fù)載均衡算法,自動(dòng)地幫助服務(wù)消費(fèi)者去請(qǐng)求。Ribbon默認(rèn)為我們提供了很多的負(fù)載均衡算法,例如輪詢、隨機(jī)等。當(dāng)然,我們也可為 Ribbon實(shí)現(xiàn)自定義的負(fù)載均衡算法。

 
2、架構(gòu)
如何在客戶端實(shí)現(xiàn)服務(wù)的負(fù)載均衡

 
3、開始使用Ribbon

 

3.1. 為microservice order增加ribbon依賴
如何在客戶端實(shí)現(xiàn)服務(wù)的負(fù)載均衡
    org.springframework.cloud    spring-cloud-starter-netflix-ribbon

其實(shí),該依賴是可以省略的,因?yàn)閟pring-cloud-starter-netflix-eureka-client中已經(jīng)包含了spring-cloud-starter-netflix-ribbon:
 如何在客戶端實(shí)現(xiàn)服務(wù)的負(fù)載均衡


3.2. 為RestTemplate設(shè)置@LoadBalanced注解

@SpringBootApplication@EnableEurekaClient@EnableFeignClientspublic class OrderApp {    public static void main(String[] args) {        SpringApplication.run(OrderApp.class, args);    }    @Bean    @LoadBalanced //使用負(fù)載均衡    public RestTemplate restTemplate() {        return new RestTemplate();    }}

3.3. 改造ItemService的實(shí)現(xiàn)

 

@Servicepublic class ItemService {    // Spring框架對(duì)RESTful方式的http請(qǐng)求做了封裝,來簡(jiǎn)化操作    @Autowired    private RestTemplate restTemplate;    public Item queryItemById(Long id) {        // 該方法走eureka注冊(cè)中心調(diào)用(去注冊(cè)中心根據(jù)app-item查找服務(wù),這種方式必須先開啟負(fù)載均衡@LoadBalanced)        String itemUrl = "http://app-item/item/{id}";        Item result = restTemplate.getForObject(itemUrl, Item.class, id);        System.out.println("訂單系統(tǒng)調(diào)用商品服務(wù),result:" + result);        return result;    }}

可以發(fā)現(xiàn),實(shí)現(xiàn)更加簡(jiǎn)化了。

這種方式關(guān)鍵在于獲取RestTemplat對(duì)象時(shí)要加上@LosdBalanced注解,否則restTemplate.getFor Object方法會(huì)報(bào)java.net.UnknownHostException: app-item,而前面一種方式是手動(dòng)指定了獲取的服務(wù)實(shí)例,不需要加此注解。

 

提示: 如果這里使用了@FeignClient完成的Restful的調(diào)用同樣也適用。

 

3.4. 重啟訂單服務(wù)進(jìn)行測(cè)試

測(cè)試結(jié)果:
 
microservice-item

3.5. 測(cè)試負(fù)載均衡

 

測(cè)試方法:
第一步,啟動(dòng)2個(gè)microservice-item服務(wù)(多個(gè)也可以):
 microservice-item

第二步,導(dǎo)入測(cè)試依賴,編寫單元測(cè)試用例:

    org.springframework.boot    spring-boot-starter-test    test

第三步,編寫測(cè)試用例:

@RunWith(SpringJUnit4ClassRunner.class)@SpringBootTest@Import(OrderApp.class)public class ItemServiceTest {    @Autowired    private LoadBalancerClient loadBalancerClient;//自動(dòng)注入    @Test    public void test() {        String serviceId = "app-item";        for (int i = 0; i < 100; i++) {            ServiceInstance serviceInstance = this.loadBalancerClient.choose(serviceId);            System.out.println("第" + (i + 1) + "次:" + serviceInstance.getHost() + ": " + serviceInstance.getPort());        }    }}

4、設(shè)置負(fù)載均衡策略

只需要在配置文件中添加配置
serviceId.ribbon.NFLoadBalancerRuleClassName=自定義的負(fù)載均衡策略類,

例如在order微服務(wù)的yml配置文件中添加:

app-item:  ribbon:    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

其中app-item是要訪問的服務(wù)id。

測(cè)試:

第1次:127.0.0.1: 8081

第2次:127.0.0.1: 9081

第3次:127.0.0.1: 9081

第4次:127.0.0.1: 8081

第5次:127.0.0.1: 9081

第6次:127.0.0.1: 8081

第7次:127.0.0.1: 9081

第8次:127.0.0.1: 9081

第9次:127.0.0.1: 8081

第10次:127.0.0.1: 8081

...

5、其它策略

接口:com.netflix.loadbalancer.IRule,其實(shí)現(xiàn)類:
策略描述:

 

AbstractLoadBalancerRule

負(fù)載均衡的抽象類,負(fù)責(zé)獲得負(fù)載均衡器并保存在內(nèi)部,通過負(fù)載均衡器維護(hù)的信息作為分配的依據(jù),并以此設(shè)計(jì)一些算法來實(shí)現(xiàn)針對(duì)特定場(chǎng)景的高效策略。


RoundRobinRule

Ribbon默認(rèn)的負(fù)載均衡機(jī)制,該策略實(shí)現(xiàn)了按照線性輪詢的方式以此選擇每個(gè)服務(wù)實(shí)例的功能。輪詢index,選擇index對(duì)應(yīng)位置的server。

 

RandomRule

該策略實(shí)現(xiàn)了從服務(wù)實(shí)例清單中隨機(jī)選擇一個(gè)服務(wù)實(shí)例的功能。通過在index上隨機(jī),選擇index對(duì)應(yīng)位置的server。


RetryRule

該策略實(shí)現(xiàn)了一個(gè)具備重試機(jī)制的實(shí)例選擇功能。該策略下在一個(gè)配置時(shí)間段內(nèi)當(dāng)選擇server不成功,則一直嘗試使用subRule的方式選擇一個(gè)可用的server。