[17.0.0.3 and later]

Liberty でのマイクロサービスの回復力の向上

MicroProfile のフォールト・トレランス・フィーチャーを使用すると、サービス呼び出しの回復力を向上させることができます。このフィーチャーは、Eclipse Microprofile Fault Tolerance 仕様 1.0 の実装です。このフィーチャーは、再試行、回路ブレーカー、バルクヘッド、タイムアウト、およびフォールバックなどのパターンによって回復力のあるマイクロサービスをサポートするプログラミング・モデルを、Failsafe オープン・ソース・ライブラリーを使用して提供します。

始める前に

MicroProfile のフォールト・トレランス・フィーチャーによって実装されるオープン・ソース MicroProfile 仕様について詳しくは、Eclipse Microprofile Fault Tolerance Specification 1.0 を参照してください。

手順

  1. server.xml ファイル内の featureManager エレメントに mpFaultTolerance-1.0 フィーチャーを追加します。
    <featureManager>
       <feature>mpFaultTolerance-1.0</feature>
    </featureManager>
  2. コード・スニペットを使用して、マイクロサービスの回復力を向上させます。
    フォールト・トレランス回路ブレーカーは、システムがフェイル・ファストを実行する方法を提供します。サービスの実行を一時的に使用不可にすることで、そのサービスがシステムに過負荷をかけるのを防ぎます。
    フォールト・トレランス RetryPolicy は、どの時点でサービスを再試行するかを構成する方法を提供します。メイン・サービスに障害が起こった場合、使用するサービスをフォールト・トレランス・フォールバックで指定できます。
    フォールト・トレランス・バルクヘッドは、サービスに対する同時呼び出しの数を制限します。また、このバルクヘッドは、サービス呼び出しが使用できるシステム・リソースの量を制限します。このコード・スニペットでは、mpFaultTolerance-1.0 フィーチャーに加えて Libertyconcurrent-1.0 フィーチャーが server.xml ファイルに指定されている必要があります。

    回路ブレーカーのコード・スニペット 1: CircuitBreaker および Timeout が構成された CircuitBreakerBean の作成。

    @RequestScoped
    public class CircuitBreakerBean {
    
        private int executionCounterA = 0;
    
        // The combined effect of the specified requestVolumeThreshold and failureRatio is that 3  
        // failures will trigger the circuit to open.
        // After a 1 second delay the Circuit will allow fresh attempts to invoke the service.
        @CircuitBreaker(delay = 1, delayUnit = ChronoUnit.SECONDS, requestVolumeThreshold = 3, failureRatio = 1.0)
        // A service is considered to have timed out after 3 seconds
        @Timeout(value = 3, unit = ChronoUnit.SECONDS)
        public String serviceA() {
            executionCounterA++;
    
            if (executionCounterA <= 3) {
                //Sleep for 10 secs to force a timeout
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    System.out.println("serviceA interrupted");
                }
            }

    回路ブレーカーのコード・スニペット 2: CircuitBreakerBean の使用。

    @Inject
    CircuitBreakerBean bean;
    
    // FaultTolerance bean with circuit breaker, should fail 3 times
    for (int i = 0; i < 3; i++) {
        try {
            bean.serviceA();
            throw new AssertionError("TimeoutException not caught");
        } catch (TimeoutException e) {
                //expected
        }
    }
    
    // The CircuitBreaker should be open, so calling serviceA should generate a 
    // CircuitBreakerOpenException.
    try {
        bean.serviceA();
        throw new AssertionError("CircuitBreakerOpenException not caught");
    } catch (CircuitBreakerOpenException e) {
        //expected
    }
    
    //allow time for the circuit to re-close
    Thread.sleep(3000);
    
    // The CircuitBreaker should be closed and serviceA should now succeed.
    String res = bean.serviceA();
    if (!"serviceA: 4".equals(res)) {
        throw new AssertionError("Bad Result: " + res);
    }

    フォールバックおよび再試行のコード・スニペット 1: FallbackHandler および Retry ポリシーが構成された FTServiceBean。

    @RequestScoped
    public class FTServiceBean {
    
        // Annotate serviceA with a named FallbackHandler and a Retry policy specifying the
        // number of retries.
        @Retry(maxRetries = 2)
        @Fallback(StringFallbackHandler.class)
        public String serviceA() {
            throw new RuntimeException("Connection failed");
            return null;
        }   
    }

    フォールバックおよび再試行のコード・スニペット 2: メイン・サービスに障害が起こった場合に駆動されるコードである FallbackHandler。

    @Dependent
    public class StringFallbackHandler implements FallbackHandler<String> {
    
        @Override
        public String handle(ExecutionContext context) {
            return "fallback for " + context.getMethod().getName();
        }
    }

    フォールバックおよび再試行のコード・スニペット 3: FTServiceBean の使用。

    private @Inject FTServiceBean ftServiceBean;
        
    try {
        // Call serviceA, which will be retried twice in the event of failure, after which
        // the FallbackHandler will be driven.
        String result = ftServiceBean.serviceA();
        if(!result.contains("serviceA"))
           throw new AssertionError("The message should be \"fallback for serviceA\"");
     }
    catch(RuntimeException ex) {
        throw new AssertionError("serviceA should not throw a RuntimeException");
    }

    バルクヘッドのコード・スニペット 1: Bulkhead が構成された BulkheadBean の作成。

    @RequestScoped
    @Asynchronous
    public class BulkheadBean {
    
        private final AtomicInteger connectATokens = new AtomicInteger(0);
    
        // Configure a Bulkhead that supports at most 2 concurrent threads.
        @Bulkhead(maxThreads = 2)
        public Future<Boolean> connectA(String data) throws InterruptedException {
            System.out.println("connectA starting " + data);
            int token = connectATokens.incrementAndGet();
            try {
                if (token > 2) {
                    throw new RuntimeException("Too many threads in connectA[" + data + "]: " + token);
                }
                Thread.sleep(5000);
                return CompletableFuture.completedFuture(Boolean.TRUE);
            } finally {
                connectATokens.decrementAndGet();
                System.out.println("connectA complete " + data);
            }
        }
    }

    バルクヘッドのコード・スニペット 2: BulkheadBean の使用。

    @Inject
    BulkheadBean bean;
    
    // connectA has a poolSize of 2
    // The first two calls to connectA should be run straight away, in parallel, each around
    // 5 seconds
    Future<Boolean> future1 = bean.connectA("One");
    Thread.sleep(100);
    Future<Boolean> future2 = bean.connectA("Two");
    Thread.sleep(100);
    
    // The next two calls to connectA should wait until the first 2 have finished
    Future<Boolean> future3 = bean.connectA("Three");
    Thread.sleep(100);
    Future<Boolean> future4 = bean.connectA("Four");
    Thread.sleep(100);
    
    //total time should be just over 10s
    Thread.sleep(11000);
    
    if (!future1.get(1000, TimeUnit.MILLISECONDS)) {
        throw new AssertionError("Future1 did not complete properly");
    }
    if (!future2.get(1000, TimeUnit.MILLISECONDS)) {
        throw new AssertionError("Future2 did not complete properly");
    }
    if (!future3.get(1000, TimeUnit.MILLISECONDS)) {
        throw new AssertionError("Future3 did not complete properly");
    }
    if (!future4.get(1000, TimeUnit.MILLISECONDS)) {
        throw new AssertionError("Future4 did not complete properly");
    }

トピックのタイプを示すアイコン タスク・トピック

ファイル名: twlp_microprofile_fault_tolerance.html