フレームワーク再試行サーキットブレーカーの構成

Spring Framework 7 は、フレームワークのレジリエンス機能の一部としてネイティブ再試行サポートを導入しました。Spring Cloud と CircuitBreaker は、Spring Framework の RetryTemplate および RetryPolicy API を使用するサーキットブレーカー実装を提供します。

Spring Framework のリトライサポートはステートレスですが、この実装では、障害を追跡し、サーキットブレーカーパターン(クローズ、オープン、ハーフオープン)を実装することで、ステートフルなサーキットブレーカー機能を追加します。この実装は、個々のリトライ回数をカウントするのではなく、1 回の実行失敗(すべてのリトライ回数の使い切り)後にサーキットブレーカーがオープンする Spring Retry の CircuitBreakerRetryPolicy をモデルにしています。

スターター

フレームワーク再試行サーキットブレーカー実装を使用するには、次のスターターをプロジェクトに追加します。

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-circuitbreaker-framework-retry</artifactId>
</dependency>
implementation 'spring-cloud-starter-circuitbreaker-framework-retry'

デフォルト構成

すべてのサーキットブレーカーにデフォルト設定を提供するには、Customizer、Bean を作成し、FrameworkRetryCircuitBreakerFactory を渡します。configureDefault メソッドを使用してデフォルト設定を提供できます。

@Bean
public Customizer<FrameworkRetryCircuitBreakerFactory> defaultCustomizer() {
	return factory -> factory.configureDefault(id -> new FrameworkRetryConfigBuilder(id)
		.retryPolicy(RetryPolicy.withMaxRetries(3))
		.openTimeout(Duration.ofSeconds(20))
		.resetTimeout(Duration.ofSeconds(5))
		.build());
}

構成オプション

FrameworkRetryConfigBuilder は次の構成オプションを提供します。

  • retryPolicy(RetryPolicy) - 再試行に使用する Spring Framework RetryPolicy。再試行の回数と条件を決定します。

  • openTimeout(Duration) - 半開状態に移行するまでの回路の開状態を維持する時間。デフォルトは 20 秒です。

  • resetTimeout(Duration) - 障害発生後、サーキットブレーカーの状態をリセットするまでの待機時間。このタイムアウト時間内に障害が発生しない場合、サーキットブレーカーは自動的にクローズ状態にリセットされます。デフォルトは 5 秒です。

特定のサーキットブレーカーの構成

デフォルト構成を提供するのと同様に、FrameworkRetryCircuitBreakerFactory を渡される Customizer Bean を作成して、特定のサーキットブレーカーを構成することもできます。

@Bean
public Customizer<FrameworkRetryCircuitBreakerFactory> slowCustomizer() {
	return factory -> factory.configure(builder -> builder
		.retryPolicy(RetryPolicy.withMaxRetries(1))
		.openTimeout(Duration.ofSeconds(30))
		.resetTimeout(Duration.ofSeconds(10))
		.build(), "slow");
}

再試行ポリシー

Spring Framework 7 provides several built-in retry policies that can be used with the Framework Retry circuit breaker:

  • RetryPolicy.withMaxRetries(int) - Retries a fixed number of times

  • RetryPolicy.withMaxDuration(Duration) - Retries until a maximum duration is reached

  • RetryPolicy.withBackoff(Duration, double) - Retries with exponential backoff

  • RetryPolicy.forExceptions(Class<?>…​) - Retries only for specific exception types

You can also combine policies using and() and or() operators:

@Bean
public Customizer<FrameworkRetryCircuitBreakerFactory> customRetryPolicy() {
	return factory -> factory.configureDefault(id -> new FrameworkRetryConfigBuilder(id)
		.retryPolicy(RetryPolicy.withMaxRetries(3)
			.and(RetryPolicy.withMaxDuration(Duration.ofSeconds(5)))
			.forExceptions(IOException.class, TimeoutException.class))
		.build());
}

Circuit Breaker Behavior

The Framework Retry circuit breaker implementation follows the Spring Retry circuit breaker pattern:

  • Closed State : Requests are allowed through and retried according to the configured RetryPolicy. When a complete invocation fails (all retries exhausted), the circuit opens immediately.

  • Open State : Requests fail immediately with a fallback response without attempting retries. After the openTimeout period, the circuit transitions to half-open.

  • Half-Open State : A single request is allowed through to test if the service has recovered. If successful, the circuit closes. If it fails, the circuit reopens.

  • Reset Timeout : If no failures occur within the resetTimeout period, the circuit breaker automatically resets to closed state, even if it was previously open.

使用例

Here’s a complete example of using the Framework Retry circuit breaker:

@Service
public class BookService {

	private final CircuitBreakerFactory circuitBreakerFactory;
	private final RestTemplate restTemplate;

	public BookService(CircuitBreakerFactory circuitBreakerFactory, RestTemplate restTemplate) {
		this.circuitBreakerFactory = circuitBreakerFactory;
		this.restTemplate = restTemplate;
	}

	public String getBookTitle(Long bookId) {
		CircuitBreaker circuitBreaker = circuitBreakerFactory.create("bookService");
		return circuitBreaker.run(
			() -> restTemplate.getForObject("/books/" + bookId, String.class),
			throwable -> "Fallback Book"
		);
	}
}

Configuration Example

@Configuration
public class CircuitBreakerConfiguration {

	@Bean
	public Customizer<FrameworkRetryCircuitBreakerFactory> defaultCustomizer() {
		return factory -> {
			// Default configuration for all circuit breakers
			factory.configureDefault(id -> new FrameworkRetryConfigBuilder(id)
				.retryPolicy(RetryPolicy.withMaxRetries(3)
					.withBackoff(Duration.ofMillis(100), 2.0))
				.openTimeout(Duration.ofSeconds(20))
				.resetTimeout(Duration.ofSeconds(5))
				.build());
		};
	}

	@Bean
	public Customizer<FrameworkRetryCircuitBreakerFactory> specificCustomizer() {
		return factory -> {
			// Specific configuration for "slow" circuit breaker
			factory.configure(builder -> builder
				.retryPolicy(RetryPolicy.withMaxRetries(1))
				.openTimeout(Duration.ofSeconds(30))
				.resetTimeout(Duration.ofSeconds(10))
				.build(), "slow");

			// Specific configuration for "critical" circuit breaker
			factory.configure(builder -> builder
				.retryPolicy(RetryPolicy.withMaxRetries(5))
				.openTimeout(Duration.ofMinutes(2))
				.resetTimeout(Duration.ofSeconds(15))
				.build(), "critical");
		};
	}
}

リアクティブサポート

The Framework Retry circuit breaker implementation does not support reactive applications. If you need reactive support, use the Resilience4J implementation instead.