[17.0.0.3 and later]

Liberty에서 마이크로서비스 복원력 향상

MicroProfile 결함 허용 기능을 사용하여 서비스 호출의 복원력을 향상시킬 수 있습니다. 이 기능은 Eclipse MicroProfile 결함 허용 스펙 1.0의 구현입니다. 이 기능은 Failsafe 개방형 소스 라이브러리를 사용하여 재시도, 회로 차단기, 벌크헤드, 제한시간 및 폴백을 포함하는 패턴을 통해 복원력 있는 마이크로서비스를 지원하는 프로그래밍 모델을 제공합니다.

시작하기 전에

MicroProfile 결함 허용 기능으로 구현되는 개방형 소스 MicroProfile 스펙에 대한 자세한 정보는 Eclipse MicroProfile 결함 허용 스펙 1.0을 참조하십시오.

프로시저

  1. server.xml 파일의 featureManager 요소에 mpFaultTolerance-1.0 기능을 추가하십시오.
    <featureManager>
       <feature>mpFaultTolerance-1.0</feature>
    </featureManager>
  2. 마이크로서비스 복원력을 향상시키기 위한 코드 스니펫을 사용하십시오.
    결함 허용 회로 차단기는 시스템이 오류를 빠르게 처리할 수 있는 방법을 제공합니다. 이 방법은 시스템의 과부하를 방지하기 위해 일시적으로 서비스를 실행할 수 없도록 합니다.
    결함 허용 재시도 정책은 서비스를 재시도하는 시점을 구성하는 방법을 제공합니다. 기본 서비스가 실패하면 결함 허용 폴백은 사용할 서비스를 지정할 수 있습니다.
    결함 허용 벌크헤드는 서비스에 대한 동시 호출 수를 제한합니다. 벌크헤드는 서비스 호출에서 사용할 수 있는 시스템 자원의 양을 제한합니다. 이 코드 스니펫을 사용하려면 server.xml 파일에 mpFaultTolerance-1.0은 물론 Libertyconcurrent-1.0 기능도 지정되어 있어야 합니다.

    회로 차단기 코드 스니펫 1: CircuitBreaker 및 제한시간이 구성되어 있는 CircuitBreakerBean 작성

    @RequestScopedpublic 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");
                }
            }

    CircuitBreaker 코드 스니펫 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 및 재시도 정책이 구성되어 있는 FTServiceBean

    @RequestScopedpublic 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

    @Dependentpublic 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: 벌크헤드가 구성되어 있는 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