1. 概述
在分佈式系統和微服務架構中,優雅地處理故障對於維持系統可靠性和性能至關重要。兩種基本彈性模式,即斷路器(Circuit Breaker)和重試(Retry),有助於實現這一目標。雖然這兩種模式都旨在提高系統穩定性和可靠性,但它們具有不同的目的,並適用於不同的場景。
在本文中,我們將深入探討這些模式,包括它們的機制、用例和在 Spring Boot 中使用 Resilience4j 的實現細節。
2. 什麼是重試?
重試模式是一種簡單但功能強大的機制,用於處理分佈式系統中發生的瞬時故障。當一個操作失敗時,重試模式會嘗試多次執行相同的操作,希望臨時問題能夠自行解決。
2.1. 重試機制的關鍵特性
重試機制圍繞着特定的屬性,使其在處理瞬時問題時有效,確保臨時故障不會演變成嚴重問題:
- 重複嘗試: 其核心思想是在指定次數內重新執行失敗的操作
- 退避策略: 這是一種高級的重試機制,包括退避策略,例如指數退避,以避免系統過載
- 適用於臨時故障: 最適合用於間歇性網絡問題、臨時服務不可用或短暫的資源限制
2.2. 使用 Resilience4j 實現重試機制示例
以下是一個使用 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. 繼電器器(Circuit Breaker)的關鍵特性
繼電器器模式旨在防止因故障服務而導致的負載過高,並減輕級聯故障的影響。下面我們來回顧其關鍵屬性:
- 狀態管理: 繼電器器有三種主要狀態:
- Closed(關閉): 正常運行狀態,允許請求繼續通過
- Open(打開): 阻止所有請求,以防止進一步的故障
- Half-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% 的故障率閾值和五次調用的滑動窗口確定電路斷路器“斷路”的條件。
- 在五次失敗嘗試後,電路會立即斷開,拒絕進一步的調用。
- 電路在等待一秒後進入半開放狀態。
- 在半開放狀態下,如果成功進行了三次調用,則電路斷路器會過渡到關閉狀態,恢復正常運行。
4. 關鍵差異:重試模式與斷路器模式
| 方面 | 重試模式 | 斷路器模式 |
|---|---|---|
| 主要目標 | 嘗試多次操作 | 防止對失效服務重複調用 |
| 故障處理 | 假設瞬時故障 | 假設潛在的系統性故障 |
| 狀態管理 | 無狀態,反覆嘗試 | 維護狀態(關閉/打開/半開) |
| 最適合用於 | 間歇性、可恢復的錯誤 | 持久性或系統性故障 |
5. 如何選擇使用每種模式
決定何時使用重試模式還是斷路器模式取決於我們的系統遇到的故障類型。這些模式相互補充,理解它們的應用有助於我們構建能夠有效處理錯誤的彈性系統。
- 使用重試模式時:
- 處理瞬時網絡問題
- 預期臨時服務不可用
- 快速恢復在幾次重試內很可能
- 使用斷路器模式時:
- 防止長時間服務故障
- 防止微服務之間的級聯故障
- 實現自我修復系統架構
在實際應用中,這些模式通常一起使用。例如,重試機制可以在斷路器關閉或半開放時工作,確保只有在斷路器關閉或半開放時才嘗試重試。
6. 最佳實踐
為了最大限度地提高這些模式的有效性:
- 監控指標: 持續監控失敗率、重試次數和電路狀態,以便進行配置微調。
- 組合模式: 使用 Retry 處理瞬態錯誤,使用 Circuit Breaker 處理系統性故障。
- 設置合理的閾值: 過度激進的閾值可能會阻礙恢復或延遲故障檢測。
- 利用庫: 使用像 Resilience4j 或 Spring Cloud Circuit Breaker 這樣的強大庫,這些庫在底層實現了 Resilience4j 和 Spring Retry,從而簡化了實現。
7. Spring Boot 集成
Spring Boot 通過其生態系統,為斷路器和重試模式提供了全面的支持。這種集成主要通過 Spring Cloud 斷路器項目和 Spring Retry 模塊實現。
Spring Cloud 斷路器項目提供了一個抽象層,允許我們在不與特定實現綁定的情況下實現斷路器。這意味着我們可以根據需要在不同的斷路器實現(如 Resilience4j、Hysterix、Sentinel 或 Spring Retry)之間切換,而無需更改應用程序代碼。 該項目利用 Spring Boot 的自動配置機制,當檢測到類路徑中的適當啓動器時,會自動配置必要的斷路器 Bean。
對於重試功能,Spring Boot 與 Spring Retry 集成,提供基於註釋和程序化的方法來實現重試邏輯。該框架通過屬性文件和 Java 配置提供靈活的配置選項,允許我們自定義重試次數、退避策略和恢復策略。
讓我們看看 Spring Boot 與這些模式的集成特性,使其特別強大:
- 自動配置支持: Spring Boot 根據類路徑中的依賴項自動配置斷路器和重試 Bean,從而減少樣板配置代碼。
- 插件式架構: 抽象層允許我們在不修改業務邏輯的情況下在不同的斷路器實現之間切換。
- 配置靈活性: 這兩種模式都可以通過應用程序屬性或 Java 配置進行配置,支持全局和針對不同服務的特定配置。
- 與 Spring 生態系統的集成: 這些模式與 RestTemplate、WebClient 等其他 Spring 組件以及各種 Spring Cloud 組件無縫集成。
- 監控和指標: Spring Boot 的 actuator 集成提供了內置的監控功能,用於監控斷路器和重試嘗試,幫助我們跟蹤彈性機制的健康和行為。
這種集成方法符合 Spring Boot 的“約定優於配置”的理念,同時保持了根據需要自定義行為的靈活性。 該框架對這些模式的支持使我們能夠構建能夠優雅地處理故障並保持系統穩定性的彈性微服務。
8. 結論
Retry 和 Circuit Breaker 都是分佈式系統中必不可少的彈性設計模式。Retry 側重於立即恢復,而 Circuit Breaker 則提供強大的防範級聯故障的保護。通過理解它們之間的差異和使用場景,我們可以設計出既可靠又容錯的系統。
藉助像 Resilience4j 和 Spring Cloud Circuit Breaker 這樣的庫,Spring Boot 提供了強大的平台,可以輕鬆實現這些模式。通過採用這些彈性策略,我們可以構建能夠優雅地承受故障的應用,即使在惡劣條件下也能確保用户體驗的流暢性。