Resilience4jのガイド
1. 概要
このチュートリアルでは、Resilience4jライブラリについて説明します。
このライブラリは、リモート通信のフォールトトレランスを管理することにより、復元力のあるシステムの実装を支援します。
ライブラリはHystrixに触発されていますが、はるかに便利なAPIと、レートリミッター(頻繁なリクエストをブロックする)、バルクヘッド(同時リクエストが多すぎるのを避ける)などの他の多くの機能を提供します。
2. Mavenセットアップ
開始するには、ターゲットモジュールをpom.xmlに追加する必要があります(例: ここにサーキットブレーカーを追加します):
io.github.resilience4j
resilience4j-circuitbreaker
0.12.1
ここでは、circuitbreaker moduleを使用しています。 すべてのモジュールとその最新バージョンはMaven Centralにあります。
次のセクションでは、ライブラリで最も一般的に使用されるモジュールについて説明します。
3. サーキットブレーカー
このモジュールでは、上記のresilience4j-circuitbreaker依存関係が必要であることに注意してください。
Circuit Breaker patternは、リモートサービスがダウンしたときの障害のカスケードを防ぐのに役立ちます。
それにAfter a number of failed attempts, we can consider that the service is unavailable/overloaded and eagerly reject all subsequent requests。 このようにして、失敗する可能性が高いコールのシステムリソースを節約できます。
Resilience4jでそれを実現する方法を見てみましょう。
まず、使用する設定を定義する必要があります。 最も簡単な方法は、デフォルト設定を使用することです:
CircuitBreakerRegistry circuitBreakerRegistry
= CircuitBreakerRegistry.ofDefaults();
カスタムパラメータを使用することもできます。
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(20)
.ringBufferSizeInClosedState(5)
.build();
ここでは、レートのしきい値を20%に設定し、5回の通話試行の最小数を設定しています。
次に、CircuitBreakerオブジェクトを作成し、それを介してリモートサービスを呼び出します。
interface RemoteService {
int process(int i);
}
CircuitBreakerRegistry registry = CircuitBreakerRegistry.of(config);
CircuitBreaker circuitBreaker = registry.circuitBreaker("my");
Function decorated = CircuitBreaker
.decorateFunction(circuitBreaker, service::process);
最後に、これがJUnitテストを通じてどのように機能するかを見てみましょう。
サービスの呼び出しを10回試みます。 呼び出しが最低5回試行され、呼び出しの20%が失敗するとすぐに停止したことを確認できるはずです。
when(service.process(any(Integer.class))).thenThrow(new RuntimeException());
for (int i = 0; i < 10; i++) {
try {
decorated.apply(i);
} catch (Exception ignore) {}
}
verify(service, times(5)).process(any(Integer.class));
3.1.サーキットブレーカのStates and Settings
CircuitBreakerは、次の3つの状態のいずれかになります。
-
CLOSED –すべてが正常で、短絡は発生しません
-
OPEN –リモートサーバーがダウンしており、リモートサーバーへのすべての要求が短絡しています
-
HALF_OPEN – OPEN状態に入ってから設定された時間が経過し、CircuitBreakerを使用すると、リモートサービスがオンラインに戻ったかどうかをリクエストで確認できます。
次の設定を構成できます。
-
CircuitBreakerが開き、短絡呼び出しを開始する失敗率のしきい値
-
CircuitBreakerが半分開いた状態に切り替わる前に開いたままにする時間を定義する待機時間
-
CircuitBreakerが半分開いているか閉じているときのリングバッファのサイズ
-
CircuitBreakerイベントを処理するカスタムCircuitBreakerEventListener
-
例外が失敗としてカウントされ、失敗率が上がるかどうかを評価するカスタムPredicate
4. レートリミッター
前のセクションと同様に、この機能にはresilience4j-ratelimiterの依存関係が必要です。
名前が示すように、this functionality allows limiting access to some service。 そのAPIはCircuitBreaker’sと非常によく似ています–Registry、Config、およびLimiterクラスがあります。
外観の例を次に示します。
RateLimiterConfig config = RateLimiterConfig.custom().limitForPeriod(2).build();
RateLimiterRegistry registry = RateLimiterRegistry.of(config);
RateLimiter rateLimiter = registry.rateLimiter("my");
Function decorated
= RateLimiter.decorateFunction(rateLimiter, service::process);
必要に応じて、レートリミッターの構成に準拠するために、装飾されたサービスブロックのすべての呼び出しが行われます。
次のようなパラメータを設定できます。
-
制限更新の期間
-
更新期間の許可制限
-
許可期間のデフォルトの待機
5. バルクヘッド
ここでは、最初にresilience4j-bulkheadの依存関係が必要になります。
to limit the number of concurrent calls to a particular service.の可能性があります
Bulkhead APIを使用して、最大数の1つの同時呼び出しを構成する例を見てみましょう。
BulkheadConfig config = BulkheadConfig.custom().maxConcurrentCalls(1).build();
BulkheadRegistry registry = BulkheadRegistry.of(config);
Bulkhead bulkhead = registry.bulkhead("my");
Function decorated
= Bulkhead.decorateFunction(bulkhead, service::process);
この構成をテストするために、モックサービスのメソッドを呼び出します。
次に、Bulkheadが他の呼び出しを許可しないことを確認します。
CountDownLatch latch = new CountDownLatch(1);
when(service.process(anyInt())).thenAnswer(invocation -> {
latch.countDown();
Thread.currentThread().join();
return null;
});
ForkJoinTask> task = ForkJoinPool.commonPool().submit(() -> {
try {
decorated.apply(1);
} finally {
bulkhead.onComplete();
}
});
latch.await();
assertThat(bulkhead.isCallPermitted()).isFalse();
次の設定を構成できます。
-
バルクヘッドによって許可される並列実行の最大量
-
飽和バルクヘッドに入ろうとするときにスレッドが待機する最大時間
6. リトライ
この機能を使用するには、resilience4j-retryライブラリをプロジェクトに追加する必要があります。
Retry APIを使用してautomatically retry a failed callできます。
RetryConfig config = RetryConfig.custom().maxAttempts(2).build();
RetryRegistry registry = RetryRegistry.of(config);
Retry retry = registry.retry("my");
Function decorated
= Retry.decorateFunction(retry, (Integer s) -> {
service.process(s);
return null;
});
次に、リモートサービス呼び出し中に例外がスローされる状況をエミュレートし、ライブラリが失敗した呼び出しを自動的に再試行するようにします。
when(service.process(anyInt())).thenThrow(new RuntimeException());
try {
decorated.apply(1);
fail("Expected an exception to be thrown if all retries failed");
} catch (Exception e) {
verify(service, times(2)).process(any(Integer.class));
}
以下を構成することもできます。
-
最大試行回数
-
再試行までの待機時間
-
障害後の待機間隔を変更するカスタム関数
-
例外によって呼び出しが再試行されるかどうかを評価するカスタムPredicate
7. キャッシュ
キャッシュモジュールには、resilience4j-cacheの依存関係が必要です。
初期化は、他のモジュールと少し異なります。
javax.cache.Cache cache = ...; // Use appropriate cache here
Cache cacheContext = Cache.of(cache);
Function decorated
= Cache.decorateSupplier(cacheContext, () -> service.process(1));
ここで、キャッシュは使用されたJSR-107 Cache実装によって行われ、Resilience4jはそれを適用する方法を提供します。
関数(Cache.decorateFunction(Function)など)を装飾するためのAPIはなく、APIはSupplierおよびCallableタイプのみをサポートすることに注意してください。
8. TimeLimiter
このモジュールでは、resilience4j-timelimiterの依存関係を追加する必要があります。
TimeLimiterを使用してlimit the amount of time spent calling a remote serviceにすることができます。
実例を示すために、1ミリ秒のタイムアウトを構成してTimeLimiterを設定しましょう。
long ttl = 1;
TimeLimiterConfig config
= TimeLimiterConfig.custom().timeoutDuration(Duration.ofMillis(ttl)).build();
TimeLimiter timeLimiter = TimeLimiter.of(config);
次に、Resilience4jが予想されるタイムアウトでFuture.get()を呼び出すことを確認しましょう。
Future futureMock = mock(Future.class);
Callable restrictedCall
= TimeLimiter.decorateFutureSupplier(timeLimiter, () -> futureMock);
restrictedCall.call();
verify(futureMock).get(ttl, TimeUnit.MILLISECONDS);
CircuitBreakerと組み合わせることもできます。
Callable chainedCallable
= CircuitBreaker.decorateCallable(circuitBreaker, restrictedCall);
9. アドオンモジュール
Resilience4jは、人気のあるフレームワークやライブラリとの統合を容易にする多くのアドオンモジュールも提供します。
よく知られている統合の一部は次のとおりです。
-
Spring Boot –resilience4j-spring-bootモジュール
-
Ratpack –resilience4j-ratpackモジュール
-
改造–resilience4j-retrofitモジュール
-
Vertx –resilience4j-vertxモジュール
-
Dropwizard –resilience4j-metricsモジュール
-
Prometheus –resilience4j-prometheusモジュール
10. 結論
この記事では、Resilience4jライブラリーのさまざまな側面を検証し、サーバー間通信におけるさまざまなフォールトトレランスの問題に対処するための使用方法を学びました。
いつものように、上記のサンプルのソースコードはover on GitHubにあります。