在上一篇中講解Eureka註冊中心的案例,我們啟動了一個user-service,然後通過DiscoveryClient來獲取服務實例信息,然後獲取ip和埠來訪問。 但是實際環境中,我們往往會開啟很多個user-service的集群。此時我們獲取的服務列表中就會有多個,到底該訪問哪一個呢? 一般這 ...
在上一篇中講解Eureka註冊中心的案例,我們啟動了一個user-service,然後通過DiscoveryClient來獲取服務實例信息,然後獲取ip和埠來訪問。
但是實際環境中,我們往往會開啟很多個user-service的集群。此時我們獲取的服務列表中就會有多個,到底該訪問哪一個呢?
一般這種情況下我們就需要編寫負載均衡演算法,在多個實例列表中進行選擇。
不過Eureka中已經幫我們集成了負載均衡組件:Ribbon,簡單修改代碼即可使用。
什麼是Ribbon:
接下來,我們就來使用Ribbon實現負載均衡。
7.1.啟動兩個服務實例
首先我們啟動兩個user-service實例,一個8081,一個8082。
Eureka監控面板:
因為Eureka中已經集成了Ribbon,所以我們無需引入新的依賴。直接修改代碼:
在RestTemplate的配置方法上添加@LoadBalanced
註解:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
修改調用方式,不再手動獲取ip和埠,而是直接通過服務名稱調用:
訪問頁面,查看結果:
完美!
7.3.源碼跟蹤
為什麼我們只輸入了service名稱就可以訪問了呢?之前還要獲取ip和埠。
顯然有人幫我們根據service名稱,獲取到了服務實例的ip和埠。它就是LoadBalancerInterceptor
我們進行源碼跟蹤:
繼續跟入execute方法:發現獲取了8082埠的服務
再跟下一次,發現獲取的是8081:
7.4.負載均衡策略
Ribbon預設的負載均衡策略是簡單的輪詢,我們可以測試一下:
編寫測試類,在剛纔的源碼中我們看到攔截中是使用RibbonLoadBalanceClient來進行負載均衡的,其中有一個choose方法,是這樣介紹的:
現在這個就是負載均衡獲取實例的方法。
我們對註入這個類的對象,然後對其測試:
結果:
符合了我們的預期推測,確實是輪詢方式。
我們是否可以修改負載均衡的策略呢?
繼續跟蹤源碼,發現這麼一段代碼:
我們看看這個rule是誰:
這不就是輪詢的意思嘛。
我們註意到,這個類其實是實現了介面IRule的,查看一下:
定義負載均衡的規則介面。
它有以下實現:
SpringBoot也幫我們提供了修改負載均衡規則的配置入口:
user-service
格式是:{服務名稱}.ribbon.NFLoadBalancerRuleClassName
,值就是IRule的實現類。
再次測試,發現結果變成了隨機:
7.5.重試機制
Eureka的服務治理強調了CAP原則中的AP,即可用性和可靠性。它與Zookeeper這一類強調CP(一致性,可靠性)的服務治理框架最大的區別在於:Eureka為了實現更高的服務可用性,犧牲了一定的一致性,極端情況下它寧願接收故障實例也不願丟掉健康實例,正如我們上面所說的自我保護機制。
但是,此時如果我們調用了這些不正常的服務,調用就會失敗,從而導致其它服務不能正常工作!這顯然不是我們願意看到的。
我們現在關閉一個user-service實例:
因為服務剔除的延遲,consumer並不會立即得到最新的服務列表,此時再次訪問你會得到錯誤提示:
但是此時,8081服務其實是正常的。
因此Spring Cloud 整合了Spring Retry 來增強RestTemplate的重試能力,當一次服務調用失敗後,不會立即拋出一次,而是再次重試另一個服務。
只需要簡單配置即可實現Ribbon的重試:
根據如上配置,當訪問到某個服務超時後,它會再次嘗試訪問下一個服務實例,如果不行就再換一個實例,如果不行,則返回失敗。切換次數取決於MaxAutoRetriesNextServer
參數的值
引入spring-retry依賴
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
我們重啟user-consumer-demo,測試,發現即使user-service2宕機,也能通過另一臺服務實例獲取到結果!