ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Reactor Resilience4J CircuitBreaker 알아보기
    카테고리 없음 2023. 8. 22. 17:53

    CircuitBreaker


    • 필요한 이유 : Circuit Breaker 는 누전차단기로 전류를 차단하는 장치를 의미한다. 누전 차단기의 역할로 사고가 발생하기 이전에 전류를 차단하여 사고를 미리 방지하는것과 같이 문제가 있는 마이크로서비스간의 트래픽을 차단하여 해당 서비스로 인한 전체 장애를 방지한다.
    • 최소요청 횟수 몇 회 이후 통계건수 기반으로 몇건을 평가했을때, 실패율이 얼마정도 되는지에 따라 CircuitBreaker가 Open 되고 이 Open 되는 시간의 지속시간을 얼만큼 유지할 것인지 등의 설정이 가능하다.
    • 서킷브레이커 동작원리 : Consumer와 Producer 사이에 Circuit Breaker 을 두고 통신하며 Circuit Breaker는 아래의 그림과 같은 3가지 상태를 가진다.
      • Close : 정상 상태
      • Open : 일정 횟수 및 일정 시간 내에 정상 응답을 받지 못할 경우 Open 상태로 변경되면서 더이상 호출 및 대기를 하지 않고 Fallback method를 호출
      • Half Open : Open 상태에서 일정 시간 이후 다시 마이크로 서비스를 호출해보는 상태를 말하며, 해당 응답 결과에 따라 Open, Close 상태를 결정한다

     

    Resilience4j


    Resilience4J : https://resilience4j.readme.io/docs/getting-started

     

    Introduction

    Resilience4j is a lightweight fault tolerance library designed for functional programming. Resilience4j provides higher-order functions (decorators) to enhance any functional interface, lambda expression or method reference with a Circuit Breaker, Rate Lim

    resilience4j.readme.io

    - 공식 가이드 문서를 보면 Resilience4J는 Hystrix에 영감을 받아 개발 되었으며, 쉬운 사용과 경량화를 지원하는 라이브러리로 소개 한다.

    - Netflix 의 Hystrix 의 경우 더이상 업그레이드 작업은 없이 유지보수만 지원하겠다고 발표(2018.11) 하여 Resilience4J 사용을 권고 한다고 한다.

     

    기본적인 동작 방식 : 2가지 (횟수 기반, 시간 기반)

    • Count-based: The count-based sliding window aggregrates the outcome of the last N calls
    • Time-based: The time-based sliding window aggregrates the outcome of the calls of the last N seconds.
    • circuit breaker을 구성하기 위해 필요한 조건으로 {} 안의 값의 정의로 동작한다.

     

     

    Failure rate and slow call rate thresholds


    • 실패율, 슬로우 쿼리 계산은 call에 대한 결과를 sliding window에 저장하여 판단한다.
    • 실패율은 최소 요청 횟수가 만족된 상태에서 계산할 수 있다. 예를 들어, 필요한 최소 요청 수가 10개인 경우 9개의 호출에 대해서 9개의 호출이 모두 실패하더라도 CircuitBreaker는 동작하지 않는다.
    • OPEN 상태시 CallNotPermittedException 에러와 함께 호출이 거부된다. HALF_OPEN 상태로 변경 되었을 시 해당 백엔드를 다시 사용할 수 있을지 확인하기 위해 구성된 호출 수 만큼 다시 허용하며 구성된 임계값보다 크거나 같으면 OPEN으로 변경되고 임계값보다 낮다면 CLOSED로 변경된다.
    • sliding window를 통해 요청 결과를 저장하거나 스냅샷을 읽을때는 동기적으로 수행된다. 오직 하나의 쓰레드만 특정 시점에 상태를 업데이트할 수 있다. 하지만 서킷 브레이커가 함수 호출을 동기화한다는 의미는 아니라고 하니 성능/병목 걱정은 하지 않아도 될듯하다
    • CircuitBreaker는 thread-safe 하며 상태는 AtomicReference에 저장된다.

     

    AtomicReference ?

    AtomicReference 클래스는 멀티쓰레드 환경에서 동시성을 보장하며 자바에서 동시성 문제를 해결하는 방법 중 하나이다. "volatile" 은 Thread1에서 쓰고, Thread2에서 읽는 경우만 동시성을 보장한다. 두개의 쓰레드에서 쓴다면 문제가 될 수 있다. "synchronized"를 쓰면 안전하게 동시성을 보장할 수 있지만 비용이 크다. Atomic 클래스는 CAS 를 이용하여 동시성을 한다. 여러 쓰레드에서 데이터를 write해도 문제가 없다.

     

     

    커스텀 CircuitBreaker 설정들


     

    resilience4j:
      circuitbreaker:
        configs:
          default:
            slidingWindowSize: 10
            failureRateThreshold: 80
            # 실패에 대한 비율 퍼센트
            # failureRateThreshold: 20
            # 지연 응답에 대한 비율 퍼센트
            # slowCallRateThreshold: 10
            # 지연에 대한 판단 시간 (ms)
            # slowCallDurationThreshold: 60000
            # Half Open 상태일 때 허가된 요청 수
            # permittedNumberOfCallsInHalfOpenState: 10
            # Half-open 상태에서 대기할 수 있는 최대 시간으로 모든 허가된 요청이 완료될 때까지 0은 무한정 기다리는 것을 의미
            # maxWaitDurationInHalfOpenState: 1000
            # COUNT_BASED 또는 TIME_BASED 로 호출의 결과를 저장하고 집계하기 위한 슬라이딩 윈도의 타입
            #slidingWindowType: COUNT_BASED
            # 상태가 CLOSED 일 때 요청의 결과를 기록하기 위한 슬라이딩 윈도의 크기
            # slidingWindowSize: 10
            # 서킷이 실패율(failure rate) 또는 지연된 응답(slow call rate)을 계산하기 전 요구되는 최소 요청의 수
            # minimumNumberOfCalls: 100
            # 서킷이 OPEN 에서 Half-open 으로 변경되기 전 대기하는 시간
            # waitDurationInOpenState: 10000
            # actuator를 통해 circuitbraker 상태를 확인하기 위해 설정
            registerHealthIndicator: true
            # 실패/성공에 대한 집계를 하지 않음
            ignoreExceptions:
              - java.lang.RuntimeException
            # 실패에 대한 집계를 하지 않는 exception
            recordExceptions:
              - org.springframework.web.client.HttpServerErrorException
              - java.util.concurrent.TimeoutException
              - java.io.IOException
          operator:
            slidingWindowSize: 10
            failureRateThreshold: 80
            registerHealthIndicator: true
        instances:
          operatorCircuitBreaker:
            baseConfig: operator
            # 사용하고자 하는 서킷브레이커의 이름
          mollyTestCircuitBreaker:
              # config 설정 값
            baseConfig: default
          polyTestCircuitBreaker:
            baseConfig: default
            failureRateThreshold: 60
    value default 설명
    recordExceptions empty 실패로 기록될 예외의 리스트로 실패율(failure rate)가 증가되는 예외 리스트이다. 예외 리스트를 설정한다면, 다른 모든 예외는 ignoreExceptions에 의해 무시되지 않는다면 성공으로 간주된다.
    ignoreExceptions empty 성공 또는 실패로 기록되지 않는 예외들의 리스트이다.
    recordException throwable -> true By default all exceptions are recored as failures. 커스텀 Predicate로 예외가 실패로 기록될지 정의 할 수 있다. 예외가 실패로 카운트 되야 한다면 true를 리턴하고 성공으로 카운트 되야 한다면 false를 리턴해야 한다. (ignoreExceptions에 의해 무시되지 않는 경우)
    ignoreException throwable -> false By default no exception is ignored. 커스텀 Predicate로 예외가 무시될지 정의할 수 있다. 예외가 무시되려면 true를 리턴하고 실패로 카운트 되려면 false를 리턴해야 한다. - 상속받아 생성된 Exception도 무시된다.

     

     

    Custom CircuitBreaker 생성 코드 & 적용 테스트


    https://resilience4j.readme.io/docs/examples-1

     

    Examples

    Examples of resilience4j-reactor

    resilience4j.readme.io

      @Bean
        fun createCustomCircuitBreaker() : CircuitBreaker {
            // 2개중 1개 실패
            val circuitBreakerConfig = CircuitBreakerConfig.custom()
                .slidingWindowSize(2)
                .failureRateThreshold(10f)
                .permittedNumberOfCallsInHalfOpenState(2)
                .build()
    
            return CircuitBreakerRegistry.of(circuitBreakerConfig)
                .circuitBreaker("mytest")
        }

     

     

    테스트


    - 블로그 api, 카페 api 요청을 기반으로 서킷 브레이커 테스트 작성

     

Designed by Tistory.