카테고리 없음

Resilience4j의 서킷 브레이커

olsohee 2024. 6. 3. 16:10

내결함성

절대 장애가 발생하지 않는 완벽한 시스템을 만드는 것은 불가능하다. 따라서 장애가 발생하더라도 견딜 수 있는 내결함성 시스템을 만드는 것이 중요하다. (* 내결함성(Fault Tolerance): 시스템의 일부 구성 요소가 작동하지 않더라도 계속 작동할 수 있는 기능)

 

특히 MSA 환경에서 각 마이크로 서비스는 동기적 통신을 통해 서비스 간 의존관계가 생기게 된다. 이로 인해 하나의 서비스 장애가 다른 모든 서비스의 장애로 전파될 수 있다. 따라서 장애 전파를 막는 것이 중요한데 그 방법으로 서킷 브레이커가 있다.

Resilience4j의 서킷 브레이커

공식 문서에서는 Resilience4j를 다음과 같이 소개한다.

Resilience4j is a fault tolerance library for Java™

Resilience4j is a lightweight fault tolerance library inspired by Netflix Hystrix, but 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 Limiter, Retry or Bulkhead. You can stack more than one decorator on any functional interface, lambda expression or method reference. The advantage is that you have the choice to select the decorators you need and nothing else.

 

Resilience4j는 함수형 프로그래밍을 위해 설계된 가벼운 내결함성 라이브러리로, Circuit Breaker, Rate Limiter, Retry, Bulkhead를 제공한다고 한다. 이 중 서킷 브레이커에 대해 알아보자.

  • States
    • 서킷 브레이커는 3가지의 보통 상태(OPEN, CLOSED, HALF_CLOSED)와 2가지의 특별한 상태(DISABLED, FORCED_OPEN)를 갖는다.
      • Closed : 요청 실패율이 정해놓은 임계치보다 낮은 상태. 평소대로 모든 요청이 처리된다.
      • Open : 요청 실패율이 정해놓은 임계치보다 높아진 상태. 서킷 브레이커가 열린 경우, 요청을 보내지 않고 즉시 실패처리 한다. 따라서 시간 초과, 예외 발생 등의 오류가 발생하지 않게 빠르게 폴백 메소드가 호출된다. 
      • Half Open : Open 이후 일정 시간이 지나면 Half Open 된다. 이 상태에서 요청이 성공하면 Closed 상태로 변경되고, 실패하면 Open 상태를 유지한다.

  • Sliding window
    • 서킷 브레이커는 슬라이딩 윈도우를 사용하여 호출 결과를 저장하고 집계한다. 개수 기반 슬라이딩 윈도우와 시간 기반 슬라이딩 윈도우 중에서 선택할 수 있는데, 개수 기반은 마지막 N개의 호출 결과를 집계하고 시간 기반은 마지막 N초 동안의 호출 결과를 집계한다.
  •  Failure rate ans slow call rate thresholds
    • 실패율 또는 느린 호출의 비율이 임계값보다 크거나 같으면 서킷 브레이커의 상태가 CLOSED에서 OPEN으로 변경된다.
    • 기본적으로 모든 실패는 예외로 간주되는데, 사용자가 별도로 특정 예외만 실패, 그 외의 예외는 성공으로 간주되도록 설정할 수도 있다. 
    • 일정 시간이 지나면 서킷 브레이커의 상태가 OPEN에서 HALF_OPEN 상태로 변경된다. 그리고 추가로 들어오는 호출 결과에 따라 다시 서킷 브레이커가 OPEN되거나 CLOSED된다.

설정

custom global CircuitBreakerConfig를 직접 생성할 수 있는데, 다음 문서를 참고하여 설정하면 된다. ➡️ https://resilience4j.readme.io/docs/circuitbreaker#create-and-configure-a-circuitbreaker 

 

CircuitBreaker

Getting started with resilience4j-circuitbreaker

resilience4j.readme.io

 

주요 설정은 다음과 같다.

  • failureRateThreshold: 호출 실패율에 대한 임계값을 설정한다. 즉 sliding window 상에서 임계값보다 실패율이 높아지면 서킷 브레이커의 상태가 OPEN된다. 기본값은 50이다.
  • slidingWindowSize: sliding window의 크기를 설정한다. 기본값은 100이다.
  • slidingWindowType: siliding window의 형태를 COUNT_BASED 또는 TIME_BASED로 설정한다. 타입에 따라서 slidingWindowSize의 숫자가 의미하는 것이 호출 횟수 혹은 시간이 된다. 기본값은 COUNT_BASED이다.
  • waitDurationInOpenState: OPEN 상태에서 얼마나 기다린 후에, HALF_OPEN으로 전환할지를 설정한다. 기본값은 60000 ms(60초)이다.

다음은 application.yml의 서킷 브레이커 설정 예시이다.

resilience4j.circuitbreaker:
  configs:
    default:
      sliding-window-type: COUNT_BASED
      slidingWindowSize: 4
      failureRateThreshold: 50
      waitDurationInOpenState: 10000

적용

서킷 브레이커를 적용하는 방법은 간단한다. 서킷 브레이커를 적용할 메소드 위에 @CircuitBreaker 어노테이션을 붙여주고, fallback 메소드를 정의하면 된다.

 

https://resilience4j.readme.io/docs/getting-started-3#fallback-methods

 

Getting Started

Getting started with resilience4j-spring-boot2 or resilience4j-spring-boot3

resilience4j.readme.io

 

위 문서에 따르면 fallback 메소드는 try/catch 블록처럼 동작한다고 한다. 즉, 어노테이션이 붙은 메소드에서 예외가 발생하면, 예외는 fallback 메소드에 의해 처리된다. 

 

이때 fallback 메소드는 어노테이션이 붙은 메소드와 같은 클래스 내에 위치해야 하며 하나의 exception 파라미터를 제외하고 나머지 메소드 시그니처가 같아야 한다. 

 

참고로, 공식 문서의 예제에서는 fallback 메소드가 private으로 설정되어 있는데, 나의 경우 private으로 설정하니 fallback 메소드에서 사용하는 스프링 빈에 대해 의존성이 주입되지 않아 Null Pointer Exception이 발생했다. 반면 fallback 메소드를 public으로 설정하니 정상 동작했다. 아마 스프링 AOP 관련 문제인 거 같은데 나중에 이 부분에 대해 다시 공부해봐야겠다..!

Reference