微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

1.負載均衡Robbin

在剛才的案例中,我們啟動了一個user-service,然後通過DiscoveryClient來獲取服務實例信息,然後獲取ip和端口來訪問。

但是實際環境中,我們往往會開啟很多個user-service的集群。此時我們獲取的服務列表中就會有多個,到底該訪問哪一個呢?

一般這種情況下我們就需要編寫負載均衡算法,在多個實例列表中進行選擇。

不過Eureka中已經幫我們集成了負載均衡組件:Ribbon,簡單修改代碼即可使用。

什麼是Ribbon:

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

接下來,我們就來使用Ribbon實現負載均衡。

1.1.啟動兩個服務實例

首先我們啟動兩個user-service實例,一個8081,一個8082。

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

Eureka監控面板:

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

1.2.開啟負載均衡

因為Eureka中已經集成了Ribbon,所以我們無需引入新的依賴。直接修改代碼:

在RestTemplate的配置方法上添加@LoadBalanced註解:

@Bean
@LoadBalanced
publicRestTemplaterestTemplate() {
 returnnewRestTemplate(newOkHttp3ClientHttpRequestFactory());
}

修改調用方式,不再手動獲取id和端口

修改調用方式,不再手動獲取ip和端口,而是直接通過服務名稱調用:
@Service
publicclassUserService{
​
 @Autowired
 privateRestTemplaterestTemplate;
​
 @Autowired
 privateDiscoveryClientdiscoveryClient;
​
 publicListqueryUserByIds(Listids) {
 Listusers=newArrayList<>();
 // 地址直接寫服務名稱即可
 StringbaseUrl="http://user-service/user/";
 ids.forEach(id->{
 // 我們測試多次查詢,
 users.add(this.restTemplate.getForObject(baseUrl+id, User.class));
 // 每次間隔500毫秒
 try{
 Thread.sleep(500);
 } catch(InterruptedExceptione) {
 e.printStackTrace();
 }
 });
 return users;
 }
}

訪問頁面查看結果。

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

完美

1.3.源碼跟蹤

為什麼我們只輸入了service名稱就可以訪問了呢?之前還要獲取ip和端口。

顯然有人幫我們根據service名稱,獲取到了服務實例的ip和端口。它就是LoadBalancerInterceptor

我們進行源碼跟蹤:

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

繼續跟入execute方法:發現獲取了8081端口的服務

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

再跟下一次,發現獲取的是8082:

1.4.負載均衡策略

Ribbon默認的負載均衡策略是簡單的輪詢,我們可以測試一下:

編寫測試類,在剛才的源碼中我們看到攔截中是使用RibbonLoadBalanceClient來進行負載均衡的,其中有一個choose方法,是這樣介紹的:

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

現在這個就是負載均衡獲取實例的方法。

我們對注入這個類的對象,然後對其測試

@RunWith(SpringRunner.class)

@SpringBootTest(classes=UserConsumerDemoApplication.class)

public class LoadBalanceTest{

@Autowired

RibbonLoadBalancerClientclient;

@Test

public void test(){

for(inti=0; i<100; i++) {

ServiceInstanceinstance=this.client.choose("user-service");

System.out.println(instance.getHost() +":"+instance.getPort());

}

}

}

結果

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

符合了我們的預期推測,確實是輪詢方式。

我們是否可以修改負載均衡的策略呢?

繼續跟蹤源碼,發現這麼一段代碼:

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

我們看看這個rule是誰:

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

這裡的rule默認值是一個RoundRobinRule,看類的介紹:

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

這不就是輪詢的意思嘛。

我們注意到,這個類其實是實現了接口IRule的,查看一下:

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

定義負載均衡的規則接口。

它有以下實現:

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

SpringBoot也幫我們提供了修改負載均衡規則的配置入口:

user-service:
 ribbon:
 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

格式是:{服務名稱}.ribbon.NFLoadBalancerRuleClassName,值就是IRule的實現類。

再次測試,發現結果變成了隨機:

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

1.5.重試機制

Eureka的服務治理強調了CAP原則中的AP,即可用性和可靠性。它與Zookeeper這一類強調CP(一致性,可靠性)的服務治理框架最大的區別在於:Eureka為了實現更高的服務可用性,犧牲了一定的一致性,極端情況下它寧願接收故障實例也不願丟掉健康實例,正如我們上面所說的自我保護機制。

但是,此時如果我們調用了這些不正常的服務,調用就會失敗,從而導致其它服務不能正常工作!這顯然不是我們願意看到的。

我們現在關閉一個user-service實例

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

因為服務剔除的延遲,consumer並不會立即得到最新的服務列表,此時再次訪問你會得到錯誤提示:

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

但是此時,8081服務其實是正常的。

因此Spring Cloud 整合了Spring Retry 來增強RestTemplate的重試能力,當一次服務調用失敗後,不會立即拋出一次,而是再次重試另一個服務。

只需要簡單配置即可實現Ribbon的重試:

spring:

cloud:

loadbalancer:

retry:

enabled: true # 開啟Spring Cloud的重試功能

user-service:

ribbon:

ConnectTimeout: 250 # Ribbon的連接超時時間

ReadTimeout: 1000 # Ribbon的數據讀取超時時間

OkToRetryOnAllOperations: true # 是否對所有操作都進行重試

MaxAutoRetriesNextServer: 1 # 切換實例的重試次數

MaxAutoRetries: 1 # 對當前實例的重試次數

根據如上配置,當訪問到某個服務超時後,它會再次嘗試訪問下一個服務實例,如果不行就再換一個實例,如果不行,則返回失敗。切換次數取決於MaxAutoRetriesNextServer參數的值

引入spring-retry依賴

 org.springframework.retry
 spring-retry

我們重啟user-consumer-demo,測試,發現即使user-service2宕機,也能通過另一臺服務實例獲取到結果!

微服務的那些事(五)-強大的負載均衡組件Robbin,看完這篇你全知道

鳴謝

感謝各位的拜讀,關於微服務,我寫了很多篇文章,我也會持續的寫下去,這是第五篇,如果你覺得還可以,請您就關注我。你的支持,是我創作的動力。


分享到:


相關文章: