目錄
- 什麼是 Resilience4j?
- 核心模組
- 快速開始
- CircuitBreaker 斷路器
- Retry 重試機制
- RateLimiter 限流器
- Bulkhead 艙壁隔離
- TimeLimiter 超時控制
- Spring Boot 整合
- 實戰範例
- 監控與指標
- 最佳實踐
- 常見問題
- 總結
什麼是 Resilience4j?
Resilience4j 是一個輕量級的容錯庫,專為 Java 8+ 和函數式編程設計,用於保護應用程式免受外部服務故障的影響。
核心特點
- 🎯 輕量級:無外部依賴,模組化設計可按需引入
- ⚡ 函數式設計:基於高階函數,與 Java 8 Lambda 完美整合
- 🔧 模組化:六大核心模組可獨立或組合使用
- 📦 Spring Boot 整合:提供 Starter 支援自動配置
- 🎨 可觀測性:內建 Metrics 支援 Prometheus、Micrometer
為什麼選擇 Resilience4j?
傳統方式的問題:
- Netflix Hystrix 已停止維護
- 缺乏容錯機制導致級聯故障
- 單一服務故障拖垮整個系統
使用 Resilience4j 的優勢:
- 積極維護的現代化替代方案
- 更輕量、更靈活的 API 設計
- 原生支援響應式編程(RxJava、Reactor)
與 Hystrix 的比較
| 特性 | Resilience4j | Hystrix |
|---|---|---|
| 維護狀態 | 活躍開發中 | 已停止維護 |
| 設計理念 | 函數式 | 命令式 |
| 依賴 | 無外部依賴 | 依賴 Archaius |
| 執行緒模型 | 可選隔離 | 強制執行緒池隔離 |
| 配置方式 | 程式碼/YAML | Archaius |
核心模組
Resilience4j 提供六個核心模組,各自解決不同的容錯場景:
| 模組 | 功能 | 使用場景 |
|---|---|---|
| CircuitBreaker | 斷路器 | 防止對故障服務的持續呼叫 |
| Retry | 重試 | 暫時性故障的自動重試 |
| RateLimiter | 限流 | 控制請求速率,保護下游服務 |
| Bulkhead | 艙壁隔離 | 限制並發,防止資源耗盡 |
| TimeLimiter | 超時控制 | 限制執行時間,快速失敗 |
| Cache | 快取 | 快取結果,減少重複呼叫 |
模組執行順序
當組合多個模組時,建議的裝飾順序(由外到內):
Retry → CircuitBreaker → RateLimiter → TimeLimiter → Bulkhead → 實際呼叫
// 組合多個模組
Supplier<String> decoratedSupplier = Decorators.ofSupplier(supplier)
.withRetry(retry)
.withCircuitBreaker(circuitBreaker)
.withRateLimiter(rateLimiter)
.withTimeLimiter(timeLimiter)
.withBulkhead(bulkhead)
.decorate();
快速開始
Maven 依賴
<!-- 核心模組 -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-circuitbreaker</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-retry</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-ratelimiter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-bulkhead</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-timelimiter</artifactId>
<version>2.2.0</version>
</dependency>
<!-- Spring Boot Starter(包含所有模組) -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
<version>2.2.0</version>
</dependency>
Gradle 依賴
// 核心模組
implementation 'io.github.resilience4j:resilience4j-circuitbreaker:2.2.0'
implementation 'io.github.resilience4j:resilience4j-retry:2.2.0'
implementation 'io.github.resilience4j:resilience4j-ratelimiter:2.2.0'
implementation 'io.github.resilience4j:resilience4j-bulkhead:2.2.0'
implementation 'io.github.resilience4j:resilience4j-timelimiter:2.2.0'
// Spring Boot 3 Starter
implementation 'io.github.resilience4j:resilience4j-spring-boot3:2.2.0'
第一個範例
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import java.time.Duration;
import java.util.function.Supplier;
public class QuickStart {
public static void main(String[] args) {
// 1. 建立 CircuitBreaker 配置
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50) // 失敗率閾值 50%
.waitDurationInOpenState(Duration.ofMillis(1000)) // 開啟狀態等待 1 秒
.slidingWindowSize(10) // 滑動窗口大小
.build();
// 2. 建立 CircuitBreaker 實例
CircuitBreaker circuitBreaker = CircuitBreaker.of("myService", config);
// 3. 裝飾函數
Supplier<String> decoratedSupplier = CircuitBreaker
.decorateSupplier(circuitBreaker, () -> callExternalService());
// 4. 執行
try {
String result = decoratedSupplier.get();
System.out.println("Result: " + result);
} catch (Exception e) {
System.out.println("Call failed: " + e.getMessage());
}
}
private static String callExternalService() {
// 模擬外部服務呼叫
return "Success";
}
}
CircuitBreaker 斷路器
斷路器是 Resilience4j 最核心的模組,用於防止對故障服務的持續呼叫。
狀態機
┌────────────────────────────────────────┐
│ │
▼ │
CLOSED ──────► OPEN ──────► HALF_OPEN ──────┘
│ │ │
│ 失敗率 │ 等待時間 │ 測試呼叫
│ 超過閾值 │ 結束後 │ 成功/失敗
│ │ │
└─────────────┴──────────────┘
| 狀態 | 說明 |
|---|---|
| CLOSED | 正常狀態,請求正常通過 |
| OPEN | 斷路狀態,快速失敗,不執行實際呼叫 |
| HALF_OPEN | 半開狀態,允許部分請求測試服務是否恢復 |
配置參數
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
// 失敗率相關
.failureRateThreshold(50) // 失敗率閾值(百分比)
.slowCallRateThreshold(100) // 慢呼叫率閾值(百分比)
.slowCallDurationThreshold(Duration.ofSeconds(2)) // 慢呼叫定義
// 滑動窗口
.slidingWindowType(SlidingWindowType.COUNT_BASED) // COUNT_BASED 或 TIME_BASED
.slidingWindowSize(10) // 窗口大小
.minimumNumberOfCalls(5) // 最小呼叫次數
// 狀態轉換
.waitDurationInOpenState(Duration.ofSeconds(10)) // OPEN 狀態等待時間
.permittedNumberOfCallsInHalfOpenState(3) // HALF_OPEN 允許的測試呼叫數
.automaticTransitionFromOpenToHalfOpenEnabled(true) // 自動轉換
// 例外處理
.recordExceptions(IOException.class, TimeoutException.class) // 記錄為失敗
.ignoreExceptions(BusinessException.class) // 忽略的例外
.build();
配置參數說明
| 參數 | 預設值 | 說明 |
|---|---|---|
failureRateThreshold |
50 | 失敗率閾值,超過則開啟斷路器 |
slowCallRateThreshold |
100 | 慢呼叫率閾值 |
slowCallDurationThreshold |
60s | 定義慢呼叫的時間閾值 |
slidingWindowType |
COUNT_BASED | 滑動窗口類型 |
slidingWindowSize |
100 | 滑動窗口大小 |
minimumNumberOfCalls |
100 | 計算失敗率前的最小呼叫數 |
waitDurationInOpenState |
60s | OPEN 狀態的等待時間 |
permittedNumberOfCallsInHalfOpenState |
10 | HALF_OPEN 狀態允許的呼叫數 |
使用範例
// 建立 CircuitBreaker
CircuitBreaker circuitBreaker = CircuitBreaker.of("paymentService", config);
// 方式 1:裝飾 Supplier
Supplier<Payment> supplier = CircuitBreaker
.decorateSupplier(circuitBreaker, () -> paymentService.process(order));
// 方式 2:裝飾 Function
Function<Order, Payment> function = CircuitBreaker
.decorateFunction(circuitBreaker, order -> paymentService.process(order));
// 方式 3:裝飾 Runnable
Runnable runnable = CircuitBreaker
.decorateRunnable(circuitBreaker, () -> notificationService.send(message));
// 方式 4:使用 Try(Vavr 函式庫)
Try<String> result = Try.ofSupplier(
CircuitBreaker.decorateSupplier(circuitBreaker, () -> callService())
);
// 方式 5:提供 Fallback
String result = circuitBreaker.executeSupplier(() -> callService());
// 或使用 Try 的 recover
Try.ofSupplier(decoratedSupplier)
.recover(throwable -> "Fallback result")
.get();
事件監聽
circuitBreaker.getEventPublisher()
.onSuccess(event -> log.info("Success: {}", event))
.onError(event -> log.error("Error: {}", event))
.onStateTransition(event ->
log.info("State changed from {} to {}",
event.getStateTransition().getFromState(),
event.getStateTransition().getToState()))
.onSlowCallRateExceeded(event -> log.warn("Slow call rate exceeded"))
.onFailureRateExceeded(event -> log.warn("Failure rate exceeded"));
Retry 重試機制
用於處理暫時性故障,自動重試失敗的操作。
配置參數
RetryConfig config = RetryConfig.custom()
.maxAttempts(3) // 最大嘗試次數
.waitDuration(Duration.ofMillis(500)) // 重試間隔
.intervalFunction(IntervalFunction.ofExponentialBackoff()) // 指數退避
.retryOnResult(response -> response.getStatus() == 500) // 根據結果重試
.retryExceptions(IOException.class, TimeoutException.class) // 重試的例外
.ignoreExceptions(BusinessException.class) // 不重試的例外
.failAfterMaxAttempts(true) // 達到最大次數後拋出例外
.build();
Retry retry = Retry.of("myRetry", config);
退避策略
// 固定間隔
RetryConfig.custom()
.waitDuration(Duration.ofMillis(500))
.build();
// 指數退避:500ms, 1000ms, 2000ms, ...
RetryConfig.custom()
.intervalFunction(IntervalFunction.ofExponentialBackoff(
Duration.ofMillis(500), // 初始間隔
2 // 乘數
))
.build();
// 隨機化指數退避(防止驚群效應)
RetryConfig.custom()
.intervalFunction(IntervalFunction.ofExponentialRandomBackoff(
Duration.ofMillis(500), // 初始間隔
2, // 乘數
Duration.ofSeconds(10) // 最大間隔
))
.build();
// 自定義間隔函數
RetryConfig.custom()
.intervalFunction(attempt -> Duration.ofMillis(attempt * 100))
.build();
使用範例
Retry retry = Retry.of("paymentRetry", config);
// 裝飾並執行
Supplier<Payment> retryingSupplier = Retry
.decorateSupplier(retry, () -> paymentService.process(order));
Try<Payment> result = Try.ofSupplier(retryingSupplier)
.recover(throwable -> Payment.failed());
// 事件監聽
retry.getEventPublisher()
.onRetry(event -> log.info("Retry attempt #{}", event.getNumberOfRetryAttempts()))
.onSuccess(event -> log.info("Success after {} attempts", event.getNumberOfRetryAttempts()))
.onError(event -> log.error("Failed after {} attempts", event.getNumberOfRetryAttempts()));
RateLimiter 限流器
控制對服務的請求速率,保護系統不被過多請求壓垮。
配置參數
RateLimiterConfig config = RateLimiterConfig.custom()
.limitRefreshPeriod(Duration.ofSeconds(1)) // 刷新週期
.limitForPeriod(10) // 每週期允許的請求數
.timeoutDuration(Duration.ofMillis(500)) // 等待許可的超時時間
.build();
RateLimiter rateLimiter = RateLimiter.of("myRateLimiter", config);
配置參數說明
| 參數 | 預設值 | 說明 |
|---|---|---|
limitRefreshPeriod |
500ns | 許可刷新週期 |
limitForPeriod |
50 | 每週期的許可數量 |
timeoutDuration |
5s | 等待許可的超時時間 |
使用範例
RateLimiter rateLimiter = RateLimiter.of("apiRateLimiter", config);
// 裝飾函數
Supplier<Response> supplier = RateLimiter
.decorateSupplier(rateLimiter, () -> apiClient.call());
// 執行(會自動等待許可)
try {
Response response = supplier.get();
} catch (RequestNotPermitted e) {
// 超時未獲得許可
log.warn("Rate limit exceeded");
}
// 動態修改限流配置
rateLimiter.changeLimitForPeriod(20);
rateLimiter.changeTimeoutDuration(Duration.ofSeconds(1));
Bulkhead 艙壁隔離
限制並發執行的數量,防止某個服務耗盡系統資源。
兩種實現方式
| 類型 | 說明 | 適用場景 |
|---|---|---|
| SemaphoreBulkhead | 信號量隔離 | 同步呼叫、低開銷 |
| ThreadPoolBulkhead | 執行緒池隔離 | 需要完全隔離、非同步執行 |
SemaphoreBulkhead(信號量)
BulkheadConfig config = BulkheadConfig.custom()
.maxConcurrentCalls(10) // 最大並發數
.maxWaitDuration(Duration.ofMillis(500)) // 等待進入的超時時間
.build();
Bulkhead bulkhead = Bulkhead.of("myBulkhead", config);
// 使用
Supplier<String> supplier = Bulkhead
.decorateSupplier(bulkhead, () -> service.call());
ThreadPoolBulkhead(執行緒池)
ThreadPoolBulkheadConfig config = ThreadPoolBulkheadConfig.custom()
.maxThreadPoolSize(10) // 最大執行緒數
.coreThreadPoolSize(5) // 核心執行緒數
.queueCapacity(20) // 佇列容量
.keepAliveDuration(Duration.ofMillis(100)) // 執行緒存活時間
.build();
ThreadPoolBulkhead bulkhead = ThreadPoolBulkhead.of("myBulkhead", config);
// 使用(返回 CompletionStage)
CompletionStage<String> stage = bulkhead.executeSupplier(() -> service.call());
TimeLimiter 超時控制
限制非同步操作的執行時間。
配置與使用
TimeLimiterConfig config = TimeLimiterConfig.custom()
.timeoutDuration(Duration.ofSeconds(2)) // 超時時間
.cancelRunningFuture(true) // 超時時取消 Future
.build();
TimeLimiter timeLimiter = TimeLimiter.of("myTimeLimiter", config);
// 裝飾 CompletableFuture Supplier
Supplier<CompletableFuture<String>> futureSupplier =
() -> CompletableFuture.supplyAsync(() -> slowService.call());
Callable<String> callable = TimeLimiter
.decorateFutureSupplier(timeLimiter, futureSupplier);
try {
String result = callable.call();
} catch (TimeoutException e) {
log.warn("Operation timed out");
}
Spring Boot 整合
依賴配置
<!-- Spring Boot 3 -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
<version>2.2.0</version>
</dependency>
<!-- AOP 支援 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Actuator(監控端點) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application.yml 配置
resilience4j:
# CircuitBreaker 配置
circuitbreaker:
instances:
paymentService:
registerHealthIndicator: true
slidingWindowSize: 10
minimumNumberOfCalls: 5
failureRateThreshold: 50
waitDurationInOpenState: 10s
permittedNumberOfCallsInHalfOpenState: 3
slidingWindowType: COUNT_BASED
recordExceptions:
- java.io.IOException
- java.util.concurrent.TimeoutException
ignoreExceptions:
- com.example.BusinessException
inventoryService:
slidingWindowSize: 20
failureRateThreshold: 60
waitDurationInOpenState: 30s
# Retry 配置
retry:
instances:
paymentService:
maxAttempts: 3
waitDuration: 500ms
enableExponentialBackoff: true
exponentialBackoffMultiplier: 2
retryExceptions:
- java.io.IOException
# RateLimiter 配置
ratelimiter:
instances:
apiLimiter:
limitRefreshPeriod: 1s
limitForPeriod: 100
timeoutDuration: 500ms
# Bulkhead 配置
bulkhead:
instances:
paymentService:
maxConcurrentCalls: 10
maxWaitDuration: 500ms
# TimeLimiter 配置
timelimiter:
instances:
paymentService:
timeoutDuration: 2s
cancelRunningFuture: true
註解使用
@Service
public class PaymentService {
// 單獨使用 CircuitBreaker
@CircuitBreaker(name = "paymentService", fallbackMethod = "paymentFallback")
public Payment processPayment(Order order) {
return paymentGateway.process(order);
}
// 組合多個模組
@CircuitBreaker(name = "paymentService", fallbackMethod = "paymentFallback")
@Retry(name = "paymentService")
@RateLimiter(name = "apiLimiter")
@Bulkhead(name = "paymentService")
@TimeLimiter(name = "paymentService")
public CompletableFuture<Payment> processPaymentAsync(Order order) {
return CompletableFuture.supplyAsync(() -> paymentGateway.process(order));
}
// Fallback 方法(參數必須一致,加上 Throwable)
private Payment paymentFallback(Order order, Throwable t) {
log.error("Payment failed for order: {}, error: {}", order.getId(), t.getMessage());
return Payment.pending(order.getId());
}
}
註解執行順序
Spring AOP 代理的執行順序(由外到內):
Retry → CircuitBreaker → RateLimiter → TimeLimiter → Bulkhead → 方法執行
可透過配置調整順序:
resilience4j:
circuitbreaker:
circuitBreakerAspectOrder: 1
retry:
retryAspectOrder: 2
ratelimiter:
rateLimiterAspectOrder: 3
實戰範例
範例 1:電商支付服務
@Service
@Slf4j
public class PaymentService {
private final PaymentGateway paymentGateway;
private final PaymentRepository paymentRepository;
@CircuitBreaker(name = "paymentGateway", fallbackMethod = "processPaymentFallback")
@Retry(name = "paymentGateway")
@TimeLimiter(name = "paymentGateway")
public CompletableFuture<PaymentResult> processPayment(PaymentRequest request) {
return CompletableFuture.supplyAsync(() -> {
log.info("Processing payment for order: {}", request.getOrderId());
PaymentResponse response = paymentGateway.charge(
request.getAmount(),
request.getCardToken()
);
Payment payment = Payment.builder()
.orderId(request.getOrderId())
.amount(request.getAmount())
.status(PaymentStatus.COMPLETED)
.transactionId(response.getTransactionId())
.build();
paymentRepository.save(payment);
return PaymentResult.success(payment);
});
}
private CompletableFuture<PaymentResult> processPaymentFallback(
PaymentRequest request, Throwable t) {
log.warn("Payment fallback triggered for order: {}, reason: {}",
request.getOrderId(), t.getMessage());
// 建立待處理的支付記錄
Payment pendingPayment = Payment.builder()
.orderId(request.getOrderId())
.amount(request.getAmount())
.status(PaymentStatus.PENDING)
.errorMessage(t.getMessage())
.build();
paymentRepository.save(pendingPayment);
return CompletableFuture.completedFuture(PaymentResult.pending(pendingPayment));
}
}
範例 2:外部 API 整合
@Component
@Slf4j
public class ExternalApiClient {
private final RestTemplate restTemplate;
private final CircuitBreaker circuitBreaker;
private final Retry retry;
private final RateLimiter rateLimiter;
public ExternalApiClient(
RestTemplate restTemplate,
CircuitBreakerRegistry circuitBreakerRegistry,
RetryRegistry retryRegistry,
RateLimiterRegistry rateLimiterRegistry) {
this.restTemplate = restTemplate;
this.circuitBreaker = circuitBreakerRegistry.circuitBreaker("externalApi");
this.retry = retryRegistry.retry("externalApi");
this.rateLimiter = rateLimiterRegistry.rateLimiter("externalApi");
}
public ApiResponse callExternalApi(ApiRequest request) {
// 組合裝飾器
Supplier<ApiResponse> decoratedSupplier = Decorators
.ofSupplier(() -> doCall(request))
.withRetry(retry)
.withCircuitBreaker(circuitBreaker)
.withRateLimiter(rateLimiter)
.decorate();
return Try.ofSupplier(decoratedSupplier)
.recover(CallNotPermittedException.class, e -> {
log.warn("Circuit breaker is open");
return ApiResponse.serviceUnavailable();
})
.recover(RequestNotPermitted.class, e -> {
log.warn("Rate limit exceeded");
return ApiResponse.tooManyRequests();
})
.recover(Exception.class, e -> {
log.error("API call failed", e);
return ApiResponse.error(e.getMessage());
})
.get();
}
private ApiResponse doCall(ApiRequest request) {
ResponseEntity<ApiResponse> response = restTemplate.postForEntity(
"https://api.external.com/endpoint",
request,
ApiResponse.class
);
return response.getBody();
}
}
範例 3:資料庫連接保護
@Repository
public class ResilientUserRepository {
private final JdbcTemplate jdbcTemplate;
private final CircuitBreaker circuitBreaker;
private final Bulkhead bulkhead;
public ResilientUserRepository(
JdbcTemplate jdbcTemplate,
CircuitBreakerRegistry circuitBreakerRegistry,
BulkheadRegistry bulkheadRegistry) {
this.jdbcTemplate = jdbcTemplate;
this.circuitBreaker = circuitBreakerRegistry.circuitBreaker("database");
this.bulkhead = bulkheadRegistry.bulkhead("database");
}
public Optional<User> findById(Long id) {
Supplier<Optional<User>> supplier = Decorators
.ofSupplier(() -> doFindById(id))
.withCircuitBreaker(circuitBreaker)
.withBulkhead(bulkhead)
.decorate();
return Try.ofSupplier(supplier)
.recover(throwable -> Optional.empty())
.get();
}
private Optional<User> doFindById(Long id) {
try {
User user = jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
new UserRowMapper(),
id
);
return Optional.ofNullable(user);
} catch (EmptyResultDataAccessException e) {
return Optional.empty();
}
}
}
監控與指標
Actuator 端點
management:
endpoints:
web:
exposure:
include: health,circuitbreakers,retries,ratelimiters,bulkheads
endpoint:
health:
show-details: always
health:
circuitbreakers:
enabled: true
可用端點:
/actuator/circuitbreakers- 所有斷路器狀態/actuator/circuitbreakers/{name}- 特定斷路器/actuator/circuitbreakerevents- 斷路器事件/actuator/retries- 重試狀態/actuator/ratelimiters- 限流器狀態/actuator/bulkheads- 艙壁狀態
Prometheus / Micrometer 整合
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-micrometer</artifactId>
<version>2.2.0</version>
</dependency>
@Configuration
public class Resilience4jMetricsConfig {
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags(
CircuitBreakerRegistry circuitBreakerRegistry,
RetryRegistry retryRegistry) {
return registry -> {
// 綁定 CircuitBreaker 指標
TaggedCircuitBreakerMetrics.ofCircuitBreakerRegistry(circuitBreakerRegistry)
.bindTo(registry);
// 綁定 Retry 指標
TaggedRetryMetrics.ofRetryRegistry(retryRegistry)
.bindTo(registry);
};
}
}
主要指標:
# CircuitBreaker 狀態
resilience4j_circuitbreaker_state{name="paymentService"}
# 失敗率
resilience4j_circuitbreaker_failure_rate{name="paymentService"}
# 呼叫次數
resilience4j_circuitbreaker_calls_seconds_count{name="paymentService",kind="successful"}
resilience4j_circuitbreaker_calls_seconds_count{name="paymentService",kind="failed"}
# Retry 嘗試次數
resilience4j_retry_calls_total{name="paymentService",kind="successful_without_retry"}
resilience4j_retry_calls_total{name="paymentService",kind="successful_with_retry"}
resilience4j_retry_calls_total{name="paymentService",kind="failed_with_retry"}
最佳實踐
1. 合理設定閾值
# ✅ 推薦:根據實際情況調整
resilience4j:
circuitbreaker:
instances:
criticalService:
failureRateThreshold: 30 # 關鍵服務,敏感閾值
slidingWindowSize: 20
minimumNumberOfCalls: 10
nonCriticalService:
failureRateThreshold: 70 # 非關鍵服務,寬鬆閾值
slidingWindowSize: 50
minimumNumberOfCalls: 20
# ❌ 不推薦:所有服務使用相同配置
2. Fallback 策略設計
// ✅ 推薦:提供有意義的降級方案
private Payment paymentFallback(Order order, Throwable t) {
if (t instanceof CallNotPermittedException) {
// 斷路器開啟,返回待處理狀態
return Payment.pending(order.getId(), "Service temporarily unavailable");
} else if (t instanceof TimeoutException) {
// 超時,記錄並稍後重試
return Payment.pendingRetry(order.getId());
}
// 其他錯誤
return Payment.failed(order.getId(), t.getMessage());
}
// ❌ 不推薦:簡單返回 null 或拋出例外
private Payment badFallback(Order order, Throwable t) {
return null; // 可能導致 NPE
}
3. 日誌與監控
@Configuration
public class Resilience4jEventConfig {
@Bean
public RegistryEventConsumer<CircuitBreaker> circuitBreakerEventConsumer() {
return new RegistryEventConsumer<>() {
@Override
public void onEntryAddedEvent(EntryAddedEvent<CircuitBreaker> event) {
CircuitBreaker cb = event.getAddedEntry();
cb.getEventPublisher()
.onStateTransition(e ->
log.info("CircuitBreaker {} state changed: {} -> {}",
cb.getName(),
e.getStateTransition().getFromState(),
e.getStateTransition().getToState()))
.onFailureRateExceeded(e ->
log.warn("CircuitBreaker {} failure rate exceeded: {}%",
cb.getName(), e.getFailureRate()));
}
// ... 其他方法
};
}
}
4. 測試策略
@SpringBootTest
class PaymentServiceTest {
@Autowired
private PaymentService paymentService;
@Autowired
private CircuitBreakerRegistry circuitBreakerRegistry;
@Test
void shouldOpenCircuitBreakerAfterFailures() {
CircuitBreaker cb = circuitBreakerRegistry.circuitBreaker("paymentService");
// 重置狀態
cb.reset();
// 模擬多次失敗
for (int i = 0; i < 10; i++) {
try {
paymentService.processPayment(failingRequest());
} catch (Exception ignored) {}
}
// 驗證斷路器已開啟
assertThat(cb.getState()).isEqualTo(CircuitBreaker.State.OPEN);
}
@Test
void shouldUseFallbackWhenCircuitBreakerIsOpen() {
CircuitBreaker cb = circuitBreakerRegistry.circuitBreaker("paymentService");
// 手動開啟斷路器
cb.transitionToOpenState();
// 執行應該使用 fallback
PaymentResult result = paymentService.processPayment(validRequest()).join();
assertThat(result.getStatus()).isEqualTo(PaymentStatus.PENDING);
}
}
常見問題
問題 1:斷路器不會開啟
症狀:失敗很多次但斷路器一直是 CLOSED 狀態
原因:
minimumNumberOfCalls未達到- 例外類型不在
recordExceptions中
解決方案:
resilience4j:
circuitbreaker:
instances:
myService:
minimumNumberOfCalls: 5 # 降低最小呼叫數
slidingWindowSize: 10
recordExceptions:
- java.lang.Exception # 記錄所有例外
問題 2:Fallback 方法沒有被呼叫
症狀:發生例外但沒有執行 fallback
原因:
- Fallback 方法簽名不正確
- 方法不在同一個類別中
解決方案:
// ✅ 正確:參數一致 + Throwable
@CircuitBreaker(name = "myService", fallbackMethod = "myFallback")
public String myMethod(String param) { ... }
private String myFallback(String param, Throwable t) { ... }
// ❌ 錯誤:缺少參數
private String myFallback(Throwable t) { ... }
// ❌ 錯誤:參數類型不一致
private String myFallback(Integer param, Throwable t) { ... }
問題 3:註解不生效
症狀:加了註解但沒有容錯效果
原因:
- 缺少 AOP 依賴
- 方法在同一類別內部呼叫(繞過代理)
解決方案:
<!-- 確保有 AOP 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
// ❌ 內部呼叫不會觸發 AOP
@Service
public class MyService {
public void methodA() {
methodB(); // 不會觸發 CircuitBreaker
}
@CircuitBreaker(name = "myService")
public void methodB() { ... }
}
// ✅ 注入自己或拆分到不同類別
@Service
public class MyService {
@Autowired
private MyService self;
public void methodA() {
self.methodB(); // 會觸發 CircuitBreaker
}
@CircuitBreaker(name = "myService")
public void methodB() { ... }
}
問題 4:如何在非 Spring 環境使用
解決方案:
// 手動建立 Registry
CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults();
RetryRegistry retryRegistry = RetryRegistry.ofDefaults();
// 取得實例
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("myService");
Retry retry = retryRegistry.retry("myService");
// 使用 Decorators
Supplier<String> decoratedSupplier = Decorators
.ofSupplier(() -> callService())
.withRetry(retry)
.withCircuitBreaker(circuitBreaker)
.decorate();
String result = decoratedSupplier.get();
總結
核心要點
Resilience4j = CircuitBreaker + Retry + RateLimiter + Bulkhead + TimeLimiter
關鍵優勢:
- ✅ 輕量級、無外部依賴
- ✅ 函數式設計、易於組合
- ✅ Spring Boot 完美整合
- ✅ 豐富的監控指標
模組選擇指南
| 場景 | 推薦模組 |
|---|---|
| 外部服務不穩定 | CircuitBreaker + Retry |
| 防止 API 被濫用 | RateLimiter |
| 資源隔離保護 | Bulkhead |
| 防止慢請求阻塞 | TimeLimiter |
| 暫時性網路故障 | Retry |
配置速查
# 最常用的 CircuitBreaker 配置
resilience4j:
circuitbreaker:
instances:
default:
failureRateThreshold: 50
slowCallRateThreshold: 80
slowCallDurationThreshold: 2s
slidingWindowSize: 10
minimumNumberOfCalls: 5
waitDurationInOpenState: 10s
permittedNumberOfCallsInHalfOpenState: 3
註解速查
@CircuitBreaker(name = "service", fallbackMethod = "fallback")
@Retry(name = "service", fallbackMethod = "fallback")
@RateLimiter(name = "service", fallbackMethod = "fallback")
@Bulkhead(name = "service", fallbackMethod = "fallback")
@TimeLimiter(name = "service", fallbackMethod = "fallback")
參考資源
- 官方文件:https://resilience4j.readme.io/
- GitHub:https://github.com/resilience4j/resilience4j
- Spring Boot 整合文件:https://resilience4j.readme.io/docs/getting-started-3
- Micrometer 指標:https://resilience4j.readme.io/docs/micrometer
建立日期:2025-12-01 最後更新:2025-12-01