以上一篇【SpringCloud入門(二):Restful API-RestTemplate(Greenwich.SR2)】為基礎來繼續。
我們知道RestTemplate是負責服務與服務之間的通訊的,但在微服務中,一般上服務都不會進行單點部署的,都會至少部署2台及以上的。現在我們有了注冊中心進行服務清單的維護,就需要一個用戶端負載均衡來進行動态服務的調用。
何為負載均衡
負載均衡(Load Balance)是分布式系統架構設計中必須考慮的因素之一,它通常是指,将請求/資料【均勻】分攤到多個操作單元上執行,負載均衡的關鍵在于【均勻】。
Ribbon
Spring Cloud Ribbon是一個基于Http和TCP的客服端負載均衡工具,它是基于Netflix Ribbon實作的。與Eureka配合使用時,Ribbon 可自動從Eureka Server (注冊中心) 擷取服務提供者位址清單,并基于負載均衡算法,通過在用戶端中配置ribbonServerList來設定服務端清單去輪詢通路以達到均衡負載的作用。
服務提供者
在microservice-provider子產品中,新增getRibbonBalancer接口。啟動8764和8765兩個端口。
idea多端口方式之一,-Dserver.port=8765
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLxYjN1UjN1ETM3ATMxkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
/**
* @describe Ribbon負載均衡測試
* @author: hero良
* @param
* @return:
*/
@GetMapping("/getRibbonBalancer")
public String getRibbonBalancer(String name){
log.debug("************接受到請求*************");
return "hello !" + name+" , this is 8764";
}
兩個執行個體都已經注冊到注冊中心
服務消費者,分兩種情況
- 項目使用了eureka
- 項目未使用eureka
1.使用eureka
pom依賴
<!--使用了eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
yml
spring:
application:
name: ribbon
server:
port: 8766
eureka:
client:
service-url:
#單機版
defaultZone: http://eureka1:8761/eureka/
#叢集模式
# defaultZone: http://eureka1:8761/eureka/,http://eureka2:8762/eureka/,http://eureka3:8763/eureka/
2.未使用eureka
需要引入ribbon依賴
<!--項目未使用eureka-->
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>-->
<!--</dependency>-->
yml
provider-service:
ribbon:
# 未使用eureka或者禁用從注冊中心擷取服務提供者資訊時,需要自己維護位址 請求的服務位址,ip:port,多個時使用 逗号 隔開
listOfServers: 127.0.0.1:8764,127.0.0.1:8765
在RestTemplate配置Bean新增 @LoadBalanced
@Bean
@LoadBalanced //開啟負載均衡注解
public RestTemplate restTemplate(){
return new RestTemplate();
}
這裡直接使用服務名調用
public String getRibbonBalancer(String name){
return restTemplate.getForObject("http://provider-service/appController/getRibbonBalancer?name="+name, String.class);
}
/**
* @description ribbon的負載均衡
* @author hero良
* @param name
* @return java.lang.String
* @exception
* @version 1.0
*/
@GetMapping("/getRibbonBalancer")
public String getRibbonBalancer(String name){
return ribbonService.getRibbonBalancer(name);
}
使用postman來測試接口8764和8765交替出現,因為Ribbon的負載政策預設是輪詢
在yml中類修改負載政策,也可以使用Bean的方式來配置。
provider-service:
ribbon:
#負載均衡政策
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #配置規則 随機
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #配置規則 輪詢
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule #配置規則 重試
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule #配置規則 響應時間權重
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.BestAvailableRule #配置規則 最空閑連接配接政策
provider-service 表示局部配置,隻針對這個服務有效,也可以使用全局配置,如下
ribbon:
#負載均衡政策
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #配置規則 随機
Ribbon的逾時、重試
RestTemplate結合Ribbon時,逾時需要配置RestTemplate的逾時,配置Ribbon時無效。
@Bean
@LoadBalanced //開啟負載均衡注解
public RestTemplate restTemplate(){
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpRequestFactory.setConnectTimeout(500);
httpRequestFactory.setReadTimeout(2000);
return new RestTemplate(httpRequestFactory);
}
//無效
serviceId:
ribbon:
ReadTimeout: 1000
ConnectTimeout: 1000
Ribbon通過增加Spring-retry還有相關配置開啟了重試,這個重試機制對于OpenFeign是不起作用的,但是對于 @LoadBalanced注解修飾的RestTemplate是有作用的。
<!-- https://mvnrepository.com/artifact/org.springframework.retry/spring-retry -->
<!--重試需要此依賴-->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.4.RELEASE</version>
</dependency>
yml配置
provider-service:
ribbon:
#負載均衡政策
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #配置規則 随機
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #配置規則 輪詢
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule #配置規則 重試
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule #配置規則 響應時間權重
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.BestAvailableRule #配置規則 最空閑連接配接政策
# 同一執行個體最大重試次數,不包括首次調用
MaxAutoRetries: 1
# 重試其他執行個體的最大重試次數,不包括首次所選的server
MaxAutoRetriesNextServer: 2
# 是否所有操作都進行重試
#當OkToRetryOnAllOperations設定為false時,隻會對get請求進行重試。
#如果設定為true,便會對所有的請求進行重試,如果是put或post等寫操作,
#如果伺服器接口沒做幂等性,會産生不好的結果,是以OkToRetryOnAllOperations慎用。
OkToRetryOnAllOperations: false
新增getRibbonTimeout接口
/**
* @description ribbon的逾時與重試
* 逾時需要在建立RestTemplate的時候指定時間,配置在配置檔案中不生效,重試需要引入Spring-retry依賴
* @author hero良
* @return java.lang.String
* @exception
* @version 1.0
*/
@GetMapping("/getRibbonTimeout")
public String getRibbonTimeout(){
return restTemplate.getForObject("http://provider-service/appController/getRibbonTimeout", String.class);
}
服務提供者
這裡使用了sleep,讓線程阻塞,達到逾時重試的效果,sleep的時間要比設定的逾時時間大,不然等于沒設定。
/**
* @describe Ribbon逾時測試
* @author: hero良
* @param
* @return:
*/
@GetMapping("/getRibbonTimeout")
public String getRibbonTimeout(){
log.debug("************接受到請求*************");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello";
}
調用後我們可以看到服務端的日志輸出
兩個服務加起來一共是6次請求,既實作了負載均衡也實作了 逾時重試
重試計算公式
MaxAutoRetries+MaxAutoRetriesNextServer+(MaxAutoRetries * MaxAutoRetriesNextServer) ,即重試5次 (不包括首次調用)一共産生6次調用。