1. 程式人生 > >Spring Cloud(二):Web服務客戶端之Ribbon

Spring Cloud(二):Web服務客戶端之Ribbon

上文介紹了服務如何通過Eureka實現註冊,以及如何從Eureka獲取已經註冊的服務列表。那麼拿到註冊服務列表後, 如何進行服務呼叫?一個簡單的實現是可以從被呼叫服務的例項列表中選擇一個服務例項,通過其hostname(或IP),埠,及API的路徑拼接成完整的url,通過http client來完成呼叫。但生產環境中,為了高效能、高可用等要素,服務的呼叫一般涉及負載均衡、故障轉移、失敗重試等實現,因此引入實現這些功能的客戶端元件也成為了微服務架構中的必備要素。Spring Cloud中可通過Ribbon與Feign來實現服務間的呼叫。

本系列文章與示例均基於最新的Spring Cloud Hoxton版編寫。

Ribbon

Ribbon是一個可實現負載均衡的Web客戶端。我們一般理解的負載均衡是在服務端實現的,如Nginx(但這都是相對的,如果相對後端服務來說,也可以把Nginx當做一個實現了負載均衡的客戶端), 而Ribbon是客戶端的負載均衡實現。

Ribbon的核心概念是命名的客戶端(named client),Spring Cloud會為每個命名客戶端建立一個子應用上下文(ApplicationContext),在該上下文中,通過RibbonClientConfiguration建立ILoadBalancer,RestClient,ServerListFilter等Bean。

Spring Cloud Netflix提供的預設的Ribbon bean及說明

Bean型別預設實現類說明
IClientConfig DefaultClientConfigImpl Ribbon客戶端配置載入實現,載入各實現bean及客戶端連線超時、通訊超時等配置
IRule ZoneAvoidanceRule 基於zone與可用性來過濾伺服器的規則實現
IPing DummyPing 判斷伺服器是否存活的實現,預設總是返回true
ServerList ConfigurationBasedServerList 獲取伺服器列表的實現,預設基於配置
ServerListFilter ZonePreferenceServerListFilter 伺服器過濾實現,預設過濾出與客戶端在同一個zone中的伺服器列表
ILoadBalancer ZoneAwareLoadBalancer 負載均衡實現,預設根據zone的請求負載量排除掉負載最高的zone,從剩下的zone中選擇一個根據給定的Rule選擇其中一個伺服器
ServerListUpdater PollingServerListUpdater 動態的伺服器列表更新器

 

Spring Cloud允許我們通過宣告一個configuration來對客戶端進行自定義,來調整或覆蓋上述預設實現,如

@Configuration
@RibbonClient(name = "custom", configuration = CustomConfiguration.class)
public class TestConfiguration {

}

這樣,客戶端將由RibbonClientConfiguration 與 CustomConfiguration中定義的元件一起組成,且CustomConfiguration 中的元件會覆蓋前者。

注意CustomConfiguration 必須是@Configuration 修飾的類,且不能被main application context的 @ComponentScan 掃描,否則會被所有@RibbonClients 共享

如果要為所有Ribbon Clients定製預設配置,則可使用@RibbonClients 註解

@RibbonClients(defaultConfiguration = DefaultRibbonConfig.class)
public class RibbonClientDefaultConfigurationTestsConfig {

}

 

也可以通過配置屬性來定製Ribbon Client,支援的配置屬性

<clientName>.ribbon.NFLoadBalancerClassName: ILoadBalancer介面實現類
<clientName>.ribbon.NFLoadBalancerRuleClassName: IRule介面實現類
<clientName>.ribbon.NFLoadBalancerPingClassName: IPing介面實現類
<clientName>.ribbon.NIWSServerListClassName: ServerList介面實現類
<clientName>.ribbon.NIWSServerListFilterClassName: ServerListFilter介面實現類

 

比如對於一個服務名稱為users的配置

users:
ribbon:
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule

 

配置屬性的優先順序 > configuration指定配置類的優先順序 > 預設RibbonClientConfiguration的優先順序, 即同樣的實現,前者覆蓋後者。

當Eureka與Ribbon同時存在時,ribbonServerList會被 DiscoveryEnabledNIWSServerList覆蓋,從Eureka來獲取server list,同時 NIWSDiscoveryPing也會替換IPing介面,代理Eureka來確定伺服器是否處於執行狀態。

Ribbon的超時與重試配置

  • <clientName>.ribbon.ConnectTimeout: 請求連線超時時間,預設2000
  • <clientName>.ribbon.ReadTimeout: 請求處理超時時間,預設5000
  • <clientName>.ribbon.MaxAutoRetries: 在同一臺伺服器上的重試次數,排除第一次呼叫,預設0
  • <clientName>.ribbon.MaxAutoRetriesNextServer: 切換伺服器的重試次數,預設1
  • <clientName>.ribbon.OkToRetryOnAllOperations: 對所有請求都進行重試,預設false

當專案中添加了Spring Retry的依賴,則會啟用重試機制。當請求失敗時,會再嘗試訪問當前伺服器(次數由MaxAutoRetries配置),如果不行,就換一個伺服器進行訪問,如果還是不行,再換伺服器訪問(更換次數由MaxAutoRetriesNextServer配置),如果還是不行,則返回請求失敗。

Ribbon的負載均衡策略

前文提到Ribbon的負載均衡預設實現為ZoneAwareLoadBalancer,那麼Ribbon提供的負載均衡策略還有哪些? 羅列如下

  • BestAvailableRule: 排除掉斷路器開啟的伺服器,選取併發請求最小的伺服器
  • AvailabilityFilteringRule: 過濾掉斷路器開啟或活躍連線數超過限制(通過<clientName>.<nameSpace>.ActiveConnectionsLimit配置,預設為Integer.MAX_VALUE)的伺服器
  • WeightedResponseTimeRule: 根據平均響應時間來動態為伺服器賦予權值,實現基於權重的輪詢
  • RetryRule: 對選擇負載均衡策略新增重試機制
  • RoundRobinRule: 簡單輪詢
  • RandomRule: 隨機輪詢
  • ZoneAvoidanceRule: 結合區域與可用性來選擇伺服器,也是預設實現

可通過如下配置修改Ribbon的負載均衡策略

client-name:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule

 

案例演示

本文案例演示基於上文搭建的springcloud-eureka 與 springcloud-eureka-client 兩個示例專案 (原始碼),依次啟動兩個專案,然後將springcloud-eureka-client專案的埠 server.port改為8081,新開一個springboot執行配置,如圖

以8081埠再起一個springcloud-eureka-client的服務例項。這是檢視Eureka頁面 http://localhost:8761/, 可以看到hello-service服務註冊了兩個例項

新建springcloud-ribbon專案 (原始碼)

pom.xml中引入依賴

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

 

編寫測試介面, LoadBalanceClient 是Ribbon的API

@RestController
public class RibbonTestController {

@Autowired
private LoadBalancerClient loadBalancer;

@GetMapping("ribbon")
public String testRibbon(){
ServiceInstance instance = loadBalancer.choose("hello-service");
return String.format("http://%s:%s", instance.getHost(),
instance.getPort());
}
}

 

啟動springcloud-ribbon, 呼叫測試介面 http://localhost:8082/ribbon, 可以看到返回結果交替顯示 http://CN-201911061714:8080, http://CN-201911061714:8081 (CN-201911061714是我電腦的hostname,你的可能不一樣),可見Ribbon實現了客戶端的負載均衡。

一些知識點

  1. Ribbon如果對所有請求進行重試,則需要保證介面的冪等性(多次呼叫產生的結果是一致的)

  2. 每一個命名的Ribbon客戶端都有一個相應的由Spring cloud維護的子應用上下文,預設是lazy load的(第一次請求客戶端時才load),可以通過如下配置更改為啟動立即載入

    ribbon:
    eager-load:
    enabled: true
    clients: client1, client2, client3
  3. client.ribbon.* 針對單個客戶端進行配置,針對所有客戶端預設配置,則使用ribbon.*

  4. 當結合斷路器使用時, 斷路器的超時時間要大於Ribbon的超時時間,不然不會觸發重試(還沒重試就觸發斷路器打開了)

  5. 除了Ribbon,能做負載均衡訪問的Web客戶端還有@LoadBalance 註解的RestTemplate, 與Feign

本文示例程式碼

 

 


認真生活,快樂分享
歡迎關注微信公眾號:空山新雨的技術空間