Resilience4jへのガイド

1概要

このチュートリアルでは、http://resilience4j.github.io/resilience4j/[Resilience4j]ライブラリについて説明します。

このライブラリは、リモート通信のフォールトトレランスを管理することによって、回復力のあるシステムを実装するのに役立ちます。

このライブラリはhttps://www.baeldung.com/introduction-to-hystrix[Hystrix]に触発されていますが、はるかに便利なAPIと、Rate Limiter(頻繁すぎるリクエストをブロックする)、Bulkhead(やめましょう)のようなその他の機能を多数提供します多数の同時リクエストなど)

2 Mavenのセットアップ

最初に、ターゲットモジュールを pom.xml に追加する必要があります(例えば、ここではサーキットブレーカーを追加します) :

<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-circuitbreaker</artifactId>
    <version>0.12.1</version>
</dependency>

ここでは、 __circuitbreaker __moduleを使用しています。すべてのモジュールとその最新版はhttps://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22io.github.resilience4j%22[Maven Central]にあります。

次のセクションでは、ライブラリの最も一般的に使用されるモジュールについて説明します。

3サーキットブレーカ

このモジュールには上記の resilience4j-circuitbreaker 依存関係が必要です。

Circuit Breaker pattern は、リモートサービスが停止したときのカスケード障害の防止に役立ちます。

  • 試行に失敗した後、サービスが利用できない/過負荷になっていると見なし、それ以降のすべての要求を積極的に拒否します。このようにして、失敗する可能性が高い呼び出し用のシステムリソースを節約できます。

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<Integer, Integer> 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. サーキットブレーカの 状態と設定

CircuitBreaker は、次の3つの状態のいずれかになります。

  • CLOSED - すべて問題ありません。短絡は発生しません。

  • OPEN - リモートサーバーがダウンしている、それに対するすべての要求が短絡されている

  • HALF OPEN__ - OPEN状態に入ってからの設定時間

が経過し、 CircuitBreaker はリモートサービスがオンラインに戻ったかどうかを確認する要求を許可します

以下の設定を構成できます。

  • CircuitBreaker が開き、それを超えると故障率のしきい値

ショートコールを開始します ** CircuitBreaker の長さを定義する待機期間

ハーフオープンに切り替わる前に開いたままにする ** CircuitBreaker が半分開いているときのリングバッファのサイズ

閉まっている ** CircuitBreaker を処理するカスタム CircuitBreakerEventListener

イベント ** カスタム Predicate は、例外が

失敗して失敗率を増加させる

4レートリミッタ

前のセクションと同様に、この機能にはhttps://search.maven.org/classic/#search%7Cga%7C1%7Ca%3A%22resilience4j-ratelimiter%22[ resilience4j-ratelimiter ]依存関係が必要です。

その名前が示すように、 この機能により、一部のサービスへのアクセスを制限することができます 。そのAPIは CircuitBreakerの に非常に似ています - Registry Config そして Limiter クラスがあります。

外観の例を次に示します。

RateLimiterConfig config = RateLimiterConfig.custom().limitForPeriod(2).build();
RateLimiterRegistry registry = RateLimiterRegistry.of(config);
RateLimiter rateLimiter = registry.rateLimiter("my");
Function<Integer, Integer> decorated
  = RateLimiter.decorateFunction(rateLimiter, service::process);

レートリミッタ設定に準拠するために必要ならば今装飾されたサービスブロックのすべてのコール。

以下のようにパラメータを設定できます。

  • 制限リフレッシュの期間

  • リフレッシュ期間の許可制限

  • デフォルトの許可待ち時間

5隔壁

ここでは、最初にhttps://search.maven.org/classic/#search%7Cga%7C1%7Cresilience4j-bulkhead[ resilience4j-bulkhead ]依存関係が必要です。

特定のサービスへの同時呼び出し数を制限することは可能です。

Bulkhead APIを使用して最大同時コール数を1つ設定する例を見てみましょう。

BulkheadConfig config = BulkheadConfig.custom().maxConcurrentCalls(1).build();
BulkheadRegistry registry = BulkheadRegistry.of(config);
Bulkhead bulkhead = registry.bulkhead("my");
Function<Integer, Integer> 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. リトライ

この機能を使用するには、https://search.maven.org/classic/#search%7Cga%7C1%7Cresilience4j-retry[ resilience4j-retry ]ライブラリをプロジェクトに追加する必要があります。

Retry APIを使用して 失敗した通話を 自動的に再試行できます。

RetryConfig config = RetryConfig.custom().maxAttempts(2).build();
RetryRegistry registry = RetryRegistry.of(config);
Retry retry = registry.retry("my");
Function<Integer, Void> 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. キャッシュ

Cacheモジュールにはhttps://search.maven.org/classic/#search%7Cga%7C1%7Cresilience4j-cache[ resilience4j-cache ]依存関係が必要です。

初期化は他のモジュールとは少し異なります。

javax.cache.Cache cache = ...;//Use appropriate cache here
Cache<Integer, Integer> cacheContext = Cache.of(cache);
Function<Integer, Integer> decorated
  = Cache.decorateSupplier(cacheContext, () -> service.process(1));

ここでキャッシングは使用されるhttps://www.baeldung.com/jcache[JSR-107 Cache]実装によって行われ、Resilience4jはそれを適用する方法を提供します。

Cache.decorateFunction(Function) のような関数を装飾するためのAPIはありません。APIは Supplier および Callable タイプのみをサポートします。

8 TimeLimiter

このモジュールでは、https://search.maven.org/classic/#search%7Cga%7C1%7Cresilience4j-timelimiter[ resilience4j-timelimiter ]依存関係を追加する必要があります。

TimeLimiterを使用して、** リモートサービスの呼び出しに費やす時間を制限することができます。

例として、タイムアウトを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__モジュール

  • ラットパック - resilience4j-ratpack モジュール

  • レトロフィット - resilience4j-retrofit モジュール

  • Vertx - resilience4j-vertx モジュール

  • Dropwizard - resilience4j-metrics モジュール

  • Prometheus - resilience4j-prometheus モジュール

10結論

この記事では、Resilience4jライブラリーのさまざまな側面について説明し、サーバー間通信におけるさまざまなフォールトトレランスの問題に対処するための使用方法を学びました。

いつものように、上記のサンプルのソースコードはhttps://github.com/eugenp/tutorials/tree/master/libraries[over GitHub]にあります。