前面分析了Eureka的使用,作為服務注冊中心,Eureka 分為 Server 端和 Client 端,Client 端作為服務的提供者,將自己注冊到 Server 端,Client端高可用的方式是使用多機部署然后注冊到Server,Server端為了保證服務的高可用,也可以使用多機部署的方式。前面簡單搭建了Eureka Client 和 Server,然后將Client成功注冊到 Server,本節我們將來看看如何調用Eureka服務,在多機部署情況下如何保證負載均衡。Spring Cloud 提供了一個組件:Ribbon。
Ribbon是客戶端負載均衡器,可以對HTTP和TCP客戶端的行為進行大量控制。Ribbon中的中心概念是指定客戶端的概念。
關於服務的負載均衡,硬要是分的話可以分兩種:
- 服務端負載均衡。將多個服務注冊到一個公共的注冊中心,服務調用者訪問注冊中心,由注冊中心提供服務的負載均衡。
- 客戶端負載均衡。將多個服務注冊到一個注冊中心,注冊中心維護一個注冊表,如果有另一個服務想要調用這個服務,那么訪問注冊中心即可,注冊中心返回注冊表信息給服務端,服務端通過特定的平衡算法來決定要調用注冊表中的哪個提供者。
服務端負載均衡:
客戶端負載均衡:
服務端負載均衡的代表性例子就是nginx,LVS。那么客戶端的負載均衡就是我們要說的Ribbon。Ribbon主要提供客戶端負載平衡算法,除此之外,Ribbon還提供:
- 服務發現集成 :功能區負載平衡器在動態環境(如雲)中提供服務發現。功能區庫中包含與Eureka和Netflix服務發現組件的集成;
- 容錯 : Ribbon API可以動態確定服務器是否已在實時環境中啟動並運行,並且可以檢測到那些已關閉的服務器;
- 可配置的負載平衡規則 : Ribbon支持開箱即用的RoundRobinRule,AvailabilityFilteringRule,WeightedResponseTimeRule,還支持定義自定義規則。
Ribbon API提供以下組件供我們使用:
- Rule :定義負載均衡策略;
- Ping : 定義如何ping目標服務實例來判斷是否存活, ribbon使用單獨的線程每隔一段時間(默認10s)對本地緩存的ServerList做一次檢查;
- ServerList :定義如何獲取服務實例列表. 兩種實現基於配置的
ConfigurationBasedServerList
和基於Eureka服務發現的DiscoveryEnabledNIWSServerList
; - ServerListFilter: 用來使用期望的特征過濾靜態配置動態獲得的候選服務實例列表. 若未提供, 默認使用
ZoneAffinityServerListFilter
; - ILoadBalancer: 定義了軟負載均衡器的操作的接口. 一個典型的負載均衡器至少需要一組用來做負載均衡的服務實例, 一個標記某個服務實例不在旋轉中的方法, 和對應的方法調用從實例列表中選出某一個服務實例;
- ServerListUpdater: DynamicServerListLoadBalancer用來更新實例列表的策略(推
EurekaNotificationServerListUpdater
/拉PollingServerListUpdater
, 默認是拉)
配置服務策略
全局策略設置
使用以下方式配置的策略表示對該項目中調用的所有服務生效。
@Configuration
public class MyConfiguration{
@Bean
public IRule ribbonRule(){
return new RandomRule();
}
//定義一個負載均衡的RestTemplate
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
上面的配置表示:
- 定義了一個隨機方式的服務調用方式,即隨即調用某個服務的提供者;
- 定義一個負載均衡的 RestTemplate,使用了 @LoadBalanced注解,該注解配合覆蓋均衡策略一起使用 RestTemplate 發出的請求才能生效。
RestTemplate是 Spring 提供的用於訪問Rest服務的客戶端模板工具集,Ribbon並沒有創建新輪子,基於此通過負載均衡配置發出HTTP請求。
ribbon的負載均衡策略主要包括以下幾種:
策略類 | 命名 | 描述 |
---|---|---|
RandomRule | 隨機策略 | 隨機選擇server |
RoundRobinRule | 輪詢策略 | 按順序選擇server |
RetryRule | 重試策略 | 在一個配置時間段內當選擇的server不成功,則繼續輪訓,一直嘗試選擇一個可用的server |
BestAvailableRule | 最低並發策略 | 先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,然后選擇一個並發量最小的服務。 |
AvailabilityFilteringRule | 可用過濾策略 | 先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,以及並發連接數超過閾值的服務,剩下的服務,使用輪詢策略 |
ResponseTimeWeightedRule | 響應時間加權策略 | 根據server的響應時間分配權重。響應時間越長,權重越低,被選擇到的概略就越低,權重越高,被選擇到的概率就越高 |
ZoneAvoidanceRule | 區域權衡策略 | 綜合判斷server所在的區域的性能和server的可用性輪詢選擇server,並且判定一個AWS Zobe的運行性能是否可用,提出不可用的Zone中所有server |
局部策略設置
如果在項目中你想對某些服務使用指定的負載均衡策略,那么可以如下配置:
@Configuration
@RibbonClients({
@RibbonClient(name = "user-service",configuration = UserServiceConfig.class),
@RibbonClient(name = "order-service",configuration = OrderServiceConfig.class)
})
public class RibbonConfig {
}
@RibbonClients 中可以包含多個@RibbonClient。每個@RibbonClient表示一個服務名,后面對應的類表示該服務配套的策略規則。
如果你只想對一個服務應用某種規則,那么可以省略:@RibbonClients:
@Configuration
@RibbonClient(name = "order-service",configuration = OrderServiceConfig.class)
public class RibbonConfig {
}
超時重試
HTTP請求不免的會有網絡不好的情況出現超時,Ribbon提供了超時重試機制,提供如下參數可以設置:
ribbon-client:
ribbon:
ConnectTimeout: 3000
ReadTimeout: 60000
MaxAutoRetries: 1 #對第一次請求的服務的重試次數
MaxAutoRetriesNextServer: 1 #要重試的下一個服務的最大數量(不包括第一個服務)
OkToRetryOnAllOperations: true
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
飢餓加載
Ribbon在進行客戶端負載均衡的時候並不是在啟動的時候就加載上下文的,實在實際請求的時候才加載,有點像servlet的第一次請求的時候才去生成實例,這回導致第一次請求會比較的緩慢,甚至可能會出現超時的情況。所以我們可以指定具體的客戶端名稱來開啟飢餓加載,即在啟動的時候便加載素養的配置項的應用上下文。
ribbon:
eager-load:
enabled: true
clients: ribbon-client-1, ribbon-client-2, ribbon-client-3
Ribbon負載均衡示例
下面看一下整合Eureka 和 Ribbon如何實現服務調用 和 負載均衡。有了Eureka之后,服務調用就無需關注服務提供者的IP。
服務的整體流程如上圖,一個集成了Ribbon的Eureka Client 從Eureka Server中獲取服務,首先拉取服務list,然后Ribbon服務會根據配置的負載均衡策略選取合適的服務提供者,向該提供者發送請求獲取結果。
工程結構如下:
一個 Eureka Server,3個Eureka Client,一個集成了Ribbon 的Consumer。整體代碼我就不貼了,已經上傳至GitHub自行下載。Demo 工程見這里:
簡單說一下關於 Ribbon consumer的配置:
pom文件中需要引入關於Ribbon的包,同時consumer也是一個Eureka Client要去拉 Eureka Server的配置,所以需要Eureka client的包。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
在啟動類中初始化了兩個bean:
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient
@SpringBootApplication
public class RibbonDemoApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonDemoApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public IRule ribbonRule() {
return new RandomRule();//這里配置策略,和配置文件對應
}
}
RestTemplate 和 IRule負載均衡策略。
然后就可以使用已經配置了負載均衡的 RestTemplate 發起請求了:
@Service
public class DemoService {
@Autowired
RestTemplate restTemplate;
public String hello(String name) {
return restTemplate.getForEntity("http://eureka-client/hello/" + name, String.class).getBody();
}
}
大家自行下載Demo工程進行調試。