1. 概述
Spring Cloud 通過使用 Netflix Ribbon 提供客户端負載均衡功能。Ribbon 的負載均衡機制可以與重試機制結合使用。
在本教程中,我們將探索此重試機制。
首先,我們將瞭解為什麼我們的應用程序需要以該功能為前提進行構建。然後,我們將構建和配置一個帶有 Spring Cloud Netflix Ribbon 的應用程序,以演示該機制。
2. 動機
在基於雲的應用中,服務通常會向其他服務發出請求。但是,在這樣一個動態且變化莫測的環境中,網絡可能會中斷或服務可能會暫時不可用。
我們希望以優雅的方式處理故障並快速恢復。 許多情況下,這些問題都是短暫的。如果我們在故障發生後不久重複發出相同的請求,也許它會成功。
這種做法有助於提高應用程序的彈性, 彈性是可靠雲應用程序的關鍵組成部分。
儘管如此,我們仍然需要關注重試,因為它們也可能導致不良情況。例如,它們可能會增加延遲,這可能不是理想的情況。
3. 設置
為了實驗重試機制,我們需要兩個 Spring Boot 服務。首先,我們將創建一個 weather-service,它將通過 REST 端點顯示今日天氣信息。
其次,我們將定義一個客户端服務,該服務將消費 weather 端點。
3.1. 天氣服務
讓我們構建一個非常簡單的天氣服務,它有時會失敗,並返回 503 HTTP 狀態碼(服務不可用)。我們將通過在調用次數為某個可配置的 <em >successful.call.divisor</em > 屬性的倍數時選擇失敗來模擬這種間歇性故障:
@Value("${successful.call.divisor}")
private int divisor;
private int nrOfCalls = 0;
@GetMapping("/weather")
public ResponseEntity<String> weather() {
LOGGER.info("Providing today's weather information");
if (isServiceUnavailable()) {
return new ResponseEntity<>(HttpStatus.SERVICE_UNAVAILABLE);
}
LOGGER.info("Today's a sunny day");
return new ResponseEntity<>("Today's a sunny day", HttpStatus.OK);
}
private boolean isServiceUnavailable() {
return ++nrOfCalls % divisor != 0;
}此外,為了幫助我們觀察對服務的重試次數,我們內部handler中包含一個消息記錄器。
稍後,我們將配置客户端服務,使其在天氣服務暫時不可用時觸發重試機制。
3.2 客户端服務
我們的第二個服務將使用 Spring Cloud Netflix Ribbon。
首先,讓我們定義 Ribbon 客户端配置:
@Configuration
@RibbonClient(name = "weather-service", configuration = RibbonConfiguration.class)
public class WeatherClientRibbonConfiguration {
@LoadBalanced
@Bean
RestTemplate getRestTemplate() {
return new RestTemplate();
}
}我們的 HTTP 客户端已使用 @LoadBalanced 註解進行標註,這意味着我們希望使用 Ribbon 進行負載均衡。
接下來,我們將添加一個 ping 機制以確定服務的可用性,並定義輪詢負載均衡策略,通過定義 RibbonConfiguration 類(位於 @RibbonClient 註解之上)。
public class RibbonConfiguration {
@Bean
public IPing ribbonPing() {
return new PingUrl();
}
@Bean
public IRule ribbonRule() {
return new RoundRobinRule();
}
}接下來,我們需要從 Ribbon 客户端關閉 Eureka,因為我們沒有使用服務發現</strong>。相反,我們使用手動定義的 weather-service</ 實例列表進行負載均衡。
因此,我們也將這些內容添加到 application.yml</ 文件中:
weather-service:
ribbon:
eureka:
enabled: false
listOfServers: http://localhost:8021, http://localhost:8022最後,讓我們創建一個控制器並使其調用後端服務:
@RestController
public class MyRestController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/client/weather")
public String weather() {
String result = this.restTemplate.getForObject("http://weather-service/weather", String.class);
return "Weather Service Response: " + result;
}
}4. 啓用重試機制
This section describes how to enable the retry mechanism for failed operations. The retry mechanism automatically retries a failed operation a specified number of times. This can be useful for handling transient errors, such as network glitches or temporary service unavailability.
To enable the retry mechanism, you can configure the following parameters:
maxRetries: The maximum number of times to retry a failed operation.retryDelay: The delay in milliseconds between retries.retryStrategy: The strategy used to determine when to retry an operation.
The following code snippet demonstrates how to enable the retry mechanism:
RetryConfig config = new RetryConfig()
.setMaxRetries(3)
.setRetryDelay(500)
.setRetryStrategy(new ExponentialBackoffRetryStrategy());
This configuration will retry the operation up to 3 times, with a delay of 500 milliseconds between each retry. The ExponentialBackoffRetryStrategy will increase the delay between retries exponentially, which can help to avoid overwhelming the service with requests.
4.1. 配置 application.yml 屬性
我們需要將天氣服務屬性放置在客户端應用程序的 application.yml 文件中:
weather-service:
ribbon:
MaxAutoRetries: 3
MaxAutoRetriesNextServer: 1
retryableStatusCodes: 503, 408
OkToRetryOnAllOperations: true上述配置使用了我們需要定義的標準Ribbon屬性,以啓用重試:
- MaxAutoRetries – 指定在同一服務器上重試失敗請求的次數(默認值為0)
- MaxAutoRetriesNextServer – 指定除了第一個服務器之外,要嘗試的服務器數量(默認值為0)
- retryableStatusCodes – 指定可重試的HTTP狀態碼列表
- OkToRetryOnAllOperations – 當此屬性設置為 true 時,所有類型的HTTP請求都會被重試,而不僅僅是GET請求(默認)
我們將重試當客户端服務收到503(服務不可用)或408(請求超時)響應碼時失敗的請求。
4.2. 必需依賴
Spring Cloud Netflix Ribbon 利用 Spring Retry 來重試失敗的請求。
我們需要確保依賴項已存在於類路徑中。否則,失敗的請求將不會被重試。由於版本由 Spring Boot 管理,因此可以省略版本號。
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>4.3. 實踐中的重試邏輯
最後,讓我們看看重試邏輯在實踐中的應用。
由於原因,我們需要兩個實例的我們的天氣服務,並將它們在 8021 和 8022 端口上運行。當然,這些實例應與上一節中定義的 listOfServers 列表相匹配。
此外,我們需要為每個實例配置 successful.call.divisor 屬性,以確保我們的模擬服務在不同時間失敗:
successful.call.divisor = 5 // instance 1
successful.call.divisor = 2 // instance 2接下來,我們還需要啓動客户端服務,並調用:
http://localhost:8080/client/weather讓我們來查看 weather-service 的控制枱:
weather service instance 1:
Providing today's weather information
Providing today's weather information
Providing today's weather information
Providing today's weather information
weather service instance 2:
Providing today's weather information
Today's a sunny day因此,經過多次嘗試(在實例 1 中 4 次,在實例 2 中 2 次),我們最終獲得了有效的響應。
5. 後退策略配置
當網絡流量超過其處理能力時,就會發生擁塞。為了緩解這種情況,我們可以設置後退策略。
默認情況下,重試之間沒有延遲。Spring Cloud Ribbon 底層使用 Spring Retry 的 NoBackOffPolicy 對象,該對象實際上什麼也不做。
但是,我們可以通過擴展 RibbonLoadBalancedRetryFactory 類來覆蓋默認行為:
@Component
private class CustomRibbonLoadBalancedRetryFactory
extends RibbonLoadBalancedRetryFactory {
public CustomRibbonLoadBalancedRetryFactory(
SpringClientFactory clientFactory) {
super(clientFactory);
}
@Override
public BackOffPolicy createBackOffPolicy(String service) {
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(2000);
return fixedBackOffPolicy;
}
}FixedBackOffPolicy 類提供了一個在重試嘗試之間固定的延遲。 如果未設置重試間隔,則默認值為 1 秒。
或者,我們可以設置指數退避策略或指數隨機退避策略:
@Override
public BackOffPolicy createBackOffPolicy(String service) {
ExponentialBackOffPolicy exponentialBackOffPolicy =
new ExponentialBackOffPolicy();
exponentialBackOffPolicy.setInitialInterval(1000);
exponentialBackOffPolicy.setMultiplier(2);
exponentialBackOffPolicy.setMaxInterval(10000);
return exponentialBackOffPolicy;
}在此,嘗試之間的初始延遲為 1 秒。然後,對於每個後續嘗試,延遲翻倍,但不超過 10 秒:1000 毫秒、2000 毫秒、4000 毫秒、8000 毫秒、10000 毫秒、10000 毫秒…
此外,ExponentialRandomBackOffPolicy 會為每個睡眠週期添加一個隨機值,而不超過下一個值。因此,它可能會產生 1500 毫秒、3400 毫秒、6200 毫秒、9800 毫秒、10000 毫秒、10000 毫秒…
選擇使用固定延遲還是隨機延遲取決於我們擁有的流量以及不同的客户端服務數量。從固定到隨機,這些策略有助於實現更好的流量峯值分佈,從而減少重試次數。例如,對於許多客户端,隨機因子有助於避免多個客户端同時打頭服務,在重試時
6. 結論
在本文中,我們學習瞭如何使用 Spring Cloud Netflix Ribbon 在我們的 Spring Cloud 應用程序中重試失敗的請求。我們還討論了這種機制提供的優勢。
接下來,我們通過一個由兩個 Spring Boot 服務支持的 REST 應用程序演示了重試邏輯的工作原理。Spring Cloud Netflix Ribbon 通過利用 Spring Retry 庫來實現這一點。
最後,我們看到了如何配置重試嘗試之間的不同類型的時間延遲。