Spring Boot 中斷路器與重試的差別
1. 概述
在分散式系統和微服務架構中,優雅地處理故障對於維護系統可靠性和效能至關重要。有助於實現這一目標的兩種基本彈性模式是斷路器和重試。雖然這兩種模式都旨在提高系統穩定性和可靠性,但它們的目的截然不同,適用於不同的場景。
在本文中,我們將深入探討這些模式,包括它們的機制、用例以及在 Spring Boot 中使用 Resilience4j 的實作細節。
2. 什麼是重試?
重試模式是一種簡單但功能強大的機制,用於處理分散式系統中的瞬時故障。當操作失敗時,重試模式會嘗試多次執行相同操作,希望臨時問題能夠自行解決。
2.1.重試的主要特徵
重試機制圍繞著特定屬性展開,這些屬性使其能夠有效處理暫時性問題,確保臨時故障不會升級為重大問題:
- 重複嘗試:核心思想是重新執行失敗的操作指定次數
- 退避策略:這是一種進階重試機制,包括退避策略,例如指數退避,這有助於避免系統不堪重負
- 臨時故障的理想選擇:最適合間歇性網路問題、臨時服務無法使用或臨時資源限制
2.2.重試實施範例
讓我們來看一個使用 Resilience4j 實作重試機制的簡單範例:
@Test
public void whenRetryWithExponentialBackoffIsUsed_thenItRetriesAndSucceeds() {
IntervalFunction intervalFn = IntervalFunction.ofExponentialBackoff(1000, 2);
RetryConfig retryConfig = RetryConfig.custom()
.maxAttempts(5)
.intervalFunction(intervalFn)
.build();
Retry retry = Retry.of("paymentRetry", retryConfig);
when(paymentService.process(1)).thenThrow(new RuntimeException("First Failure"))
.thenThrow(new RuntimeException("Second Failure"))
.thenReturn("Success");
Callable<String> decoratedCallable = Retry.decorateCallable(
retry, () -> paymentService.processPayment(1)
);
try {
String result = decoratedCallable.call();
assertEquals("Success", result);
} catch (Exception ignored) {
}
verify(paymentService, times(3)).processPayment(1);
}
在這個例子中:
- 重試機制最多嘗試操作五次
- 它採用指數退避策略在嘗試之間引入延遲,從而降低系統過載的風險
- 重試兩次後操作成功
3.什麼是斷路器模式?
斷路器模式是處理故障的更進階方法。它可以防止應用程式重複嘗試執行可能失敗的操作,從而防止級聯故障並提供系統穩定性。
3.1.斷路器的主要特性
斷路器模式的重點是防止故障服務負載過大並減輕級聯故障。讓我們回顧一下它的關鍵屬性:
- 狀態管理:斷路器有三個主要狀態:
- 已關閉:正常操作,允許請求繼續進行
- Open :阻止所有請求以防止進一步失敗
- 半開放:允許有限數量的測試請求來檢查系統是否已恢復
- 失敗閾值:監控滑動視窗內失敗請求的百分比,並在失敗率超過配置的閾值時「跳閘」電路
- 防止級聯故障:停止重複呼叫失敗的服務,保護整個系統免於降級
3.2.斷路器實施範例
以下是展示狀態轉換的斷路器實作的簡單範例:
@Test
public void whenCircuitBreakerTransitionsThroughStates_thenBehaviorIsVerified() {
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.slidingWindowSize(5)
.permittedNumberOfCallsInHalfOpenState(3)
.build();
CircuitBreaker circuitBreaker = CircuitBreaker.of("paymentCircuitBreaker", circuitBreakerConfig);
AtomicInteger callCount = new AtomicInteger(0);
when(paymentService.processPayment(anyInt())).thenAnswer(invocationOnMock -> {
callCount.incrementAndGet();
throw new RuntimeException("Service Failure");
});
Callable<String> decoratedCallable = CircuitBreaker.decorateCallable(
circuitBreaker, () -> paymentService.processPayment(1)
);
for (int i = 0; i < 10; i++) {
try {
decoratedCallable.call();
} catch (Exception ignored) {
}
}
assertEquals(5, callCount.get());
assertEquals(CircuitBreaker.State.OPEN, circuitBreaker.getState());
callCount.set(0);
circuitBreaker.transitionToHalfOpenState();
assertEquals(CircuitBreaker.State.HALF_OPEN, circuitBreaker.getState());
reset(paymentService);
when(paymentService.processPayment(anyInt())).thenAnswer(invocationOnMock -> {
callCount.incrementAndGet();
return "Success";
});
for (int i = 0; i < 3; i++) {
try {
decoratedCallable.call();
} catch (Exception ignored) {
}
}
assertEquals(3, callCount.get());
assertEquals(CircuitBreaker.State.CLOSED, circuitBreaker.getState());
}
在這個例子中:
- 50% 的故障率閾值和五次調用的滑動視窗決定了斷路器何時「跳閘」。
- 五次嘗試失敗後,電路斷開,立即拒絕進一步的呼叫。
- 等待一秒後,電路轉換為半開。
- 在半開狀態下,進行了 3 次成功調用,導致斷路器轉換為閉合狀態,恢復正常操作。
4. 主要差異:重試與斷路器
方面 | 重試模式 | 斷路器模式 |
---|---|---|
主要目標 | 多次嘗試操作 | 防止重複呼叫失敗的服務 |
故障處理 | 假設瞬時故障 | 假設潛在的系統故障 |
狀態管理 | 無狀態,重複嘗試 | 保持狀態(關閉/開啟/半開啟) |
最適合用於 | 間歇性、可恢復的錯誤 | 持續性或系統性故障 |
5. 何時使用每種模式
決定何時使用重試或斷路器取決於我們的系統遇到的故障類型。這些模式相輔相成,了解它們的應用程式可以幫助我們建立有效處理錯誤的彈性系統。
- 在以下情況下使用重試:
- 處理暫時性網路問題
- 預計服務暫時無法使用
- 重試幾次即可快速恢復
- 在以下情況下使用斷路器:
- 防止長期服務故障
- 防止微服務中的級聯故障
- 實施自我修復系統架構
在實際應用中,這些模式經常一起使用。例如,重試機制可以在斷路器的範圍內工作,確保僅在電路閉合或半開時才嘗試重試。
6. 最佳實踐
為了最大限度地發揮這些模式的有效性:
- 監控指標:持續監控故障率、重試嘗試和電路狀態以微調配置。
- 組合模式:對瞬時錯誤使用重試,對系統故障使用斷路器。
- 設定切合實際的閾值:過於激進的閾值可能會阻礙恢復或延遲故障檢測。
- 利用函式庫:使用 Resilience4j 或 Spring Cloud Circuit Breaker 等強大的函式庫,它們在底層實作了 Resilience4j 和 Spring Retry,以簡化實作。
7. Spring Boot 集成
Spring Boot 透過其生態系統為斷路器和重試模式提供全面支援。這種整合主要是透過Spring Cloud Circuit Breaker專案和Spring Retry模組來實現的。
Spring Cloud Circuit Breaker 專案提供了一個抽象層,使我們能夠實作斷路器,而無需綁定到特定的實作。這意味著我們可以根據需要在不同的斷路器實作(例如 Resilience4j、Hysterix、Sentinel 或 Spring Retry)之間切換,而無需更改應用程式程式碼。此專案使用 Spring Boot 的自動配置機制,當它在類別路徑中偵測到適當的啟動器時,它會自動配置必要的斷路器 bean。
對於重試功能,Spring Boot 與 Spring Retry 集成,提供基於註釋和程式設計的方法來實現重試邏輯。此框架透過屬性檔案和Java配置提供靈活的設定選項,讓我們可以自訂重試嘗試、退避策略和復原策略。
讓我們來看看 Spring Boot 與這些模式整合的一些特徵,這些特徵使其特別強大:
- 自動配置支援: Spring Boot 根據類別路徑中的依賴項自動配置斷路器和重試 bean,從而減少樣板配置程式碼。
- 可插拔架構:抽象層允許我們在不同的斷路器實作之間切換,而無需修改我們的業務邏輯。
- 配置靈活:兩種模式都可以透過應用程式屬性或Java配置進行配置,支援不同服務的全域配置和特定配置。
- 與 Spring 生態系統整合:這些模式與其他 Spring 組件(如
RestTemplate
、WebClient
和各種 Spring Cloud 組件)無縫協作。 - 監控與指標: Spring Boot 的致動器集成為斷路器和重試嘗試提供內建監控功能,幫助我們追蹤彈性機制的運作狀況和行為。
這種整合方法符合Spring Boot 約定優於配置的理念,同時保持在需要時自訂行為的靈活性。該框架對這些模式的支援使得建立彈性微服務變得更加容易,這些微服務可以優雅地處理故障並保持系統穩定性。
八、結論
重試和斷路器都是分散式系統中必不可少的彈性模式。重試著重於立即恢復,而斷路器則針對級聯故障提供強大的保護。透過了解它們的差異和用例,我們可以設計出既可靠又容錯的系統。
透過 Resilience4j 和 Spring Cloud Circuit Breaker 等程式庫,Spring Boot 提供了一個強大的平台來輕鬆實現這些模式。透過採用這些彈性策略,我們可以建立能夠優雅地承受故障的應用程序,即使在不利的條件下也能確保無縫的用戶體驗。
與往常一樣,完整的程式碼以及本文中提供的所有範例都可以在 GitHub 上找到。