JAVA/기타

Springboot에서 CircuitBreaker 설명 및 사용법

99C0RN 2022. 11. 29. 20:28

CircuitBreaker(서킷브레이커)란?

Fault Tolerance(=장애 허용 시스템)에서 사용되는 대표적인 패턴으로써 서비스에서 타 서비스 호출 시 에러, 응답지연, 무응답, 일시적인 네트워크 문제 등을 요청이 무작위로 실패하는 경우에 Circuit을 오픈하여 메세지가 다른 서비스로 전파되지 못하도록 막고 미리 정의해놓은 Fallback Response를 보내어 서비스 장애가 전파되지 않도록 하는 패턴 (대표적으로 MSA 환경에서 사용)

서킷브레이커 종류

  • Netflix Hystrix
    • https://github.com/Netflix/Hystrix
    • 넷플릭스에서 만든 라이브러리로 MSA 환경에서 분산된 서비스간 통신이 원활하지 않은 경우에 각 서비스가 장애 내성과 지연 내성을 갖게하도록 도와주는 라이브러리
    • 현재는 지원종료 상태이며 Resilience4j 가 권장되는 상태
      • Resilience4j 권고
      • Spring Boot 2.4.X 부터는 더 이상 지원하지 않음
  • Resilience4j
    • Resilience4j 는 Netflix Hystrix 로 부터 영감을 받은 Fault Tolerance Libray
    • 사용하기 가볍고 다른 라이브러리 의존성이 없음
    • Java 전용으로 개발된 경량화된 Fault Tolerance Libray

CircuitBreaker 간단한 흐름 설명

  • CircuitBreaker는 다량의 오류를 감지하면 서킷을 열어 새 호출을 받지 않는다.
  • CircuitBreaker는 서킷이 열려 있을 때 빠른 실패 로직을 수행한다.
    즉, 이어지는 호출에서 시간 초과, 예외 발생 등 오류가 발생하지 않게, 폴백 메서드 호출을 리다이렉션을 한다.
    Fallback 메서드에서 다양한 비즈니스 로직을 적용하면 로컬 캐시의 데이터를 반환하거나 즉각적인 오류 메시지를 반환하는 등 최적화된 응답을 생성할 수 있다.
    이로써 의존하는 서비스의 응답 중단 때문에 마이크로 서비스가 응답하지 못하게 되는 문제를 방지할 수 있는다.
  • 시간이 지나면 CircuitBreaker는 반열림 상태로 전환돼 새로운 호출을 허용하며, 이를 통해 문제를 일으킨 원인이 사라졌는지 확인한다.
  • CircuitBreaker는 새로운 오류를 감지하면서 서킷을 다시 열고 빠른 실패 로직을 다시 수행하며, 오류가 사라졌으면 서킷을 닫고 정상 작동 상태로 돌아간다.

사용법

테스트 환경: spring boot 2.7 + java17 + gradle

 

# gradle 의존성(resilience4j, actuator) 추가

dependencies {
    // resilience4j(+ actuator & aop)
    implementation "io.github.resilience4j:resilience4j-spring-boot2:1.7.0"
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-aop'
}

 

# application.yml(or .properties) 설정 추가(다음 탭에서 설정값에 대한 자세한 설명 작성)

# resilience4j
resilience4j.circuitbreaker:
  configs:
    default:
      failureRateThreshold: 50  # 실패율 임계값(%)
      slowCallRateThreshold: 80 # 느린호출 임계값(%)
      slowCallDurationThreshold: 1000  # 느린호출 판단 기준 임계값(ms)
      permittedNumberOfCallsInHalfOpenState: 5  # 서킷브레이커 상태가 half-open일 때, 호출 수 설정값
      maxWaitDurationInHalfOpenState: 5000  # 서킷브레이커가 open으로 전환되기전 half-open 상태를 유지할 최대 시간(ms)
      slidingWindowType: COUNT_BASED
      slidingWindowSize: 10
      minimumNumberOfCalls: 10  # 실패율 or 느린호출을 계산하기 전 필요한 최소 호출수
      waitDurationInOpenState: 5000  # 서킷브레이커가 open에서 half-open 상태로 전환하기까지 기다려야 되는 시간(ms)
      registerHealthIndicator: true  # actuator/health에 서킷브레이커 상태 노출
  instances:
    myInstance1:  # 인스턴스별로 설정을 다르게 설정가능
      baseConfig: default

 

 

# @CricuitBreaker - 적용하고자 하는 메서드에 서킷브레이커 적용

public class TestService {
    @CircuitBreaker(name = "myInstance1", fallbackMethod = "getMysqlData")
    public AnimalDto getRedisData(String key) {
    	// redis에서 동물 데이터 조회
    }
 
    public AnimalDto getMysqlData(String key, Throwable t) {
        // Mysql db에서 동물 데이터 조회
    }
}

@CircuitBreaker 어노테이션
name: 서킷브레이커 설정 정보(application.yml 내) 사용할 인스턴스명 설명

(ex: resilience4j.circuitbreaker.instances.myInstance1)
fallbackMethod: 서킷브레이커 개방(open, 에러상황)시, 호출될 method 설정(ex: getMysqlData())

 

 

위 내용들만 추가하면 서킷브레이커를 사용하기 위한 기본적인 설정은 끝


CircuitBreaker 설정 프로퍼티(application.yml or cofing class) 목록

공식 문서: https://resilience4j.readme.io/docs/circuitbreaker

Config Property Default Value Description
failureRateThreshold 50 실패율 임계값을 백분율(단위: %)로 구성

실패율이 임계값보다 크거나 같을 때(실패율 >= 임계값)
CircuitBreaker의 상태는 개방(Open)으로 전환하고, 단락 호출을 시작
slowCallRateThreshold 100 느린 호출(slow call)의 임계값을 백분율(단위: %)로 구성
(CircuitBreaker는 호출 지속 시간이 설정된 slowCallDurationThreshold 값보다 클 경우 호출이 느리다고 간주합니다.)

느린 호출의 비율이 임계값보다 크거나 같을 때(느린호출 비율 >= 임계값)
CircuitBreaker의 상태는 개방(Open)으로 전환하고, 단락 호출을 시작
slowCallDurationThreshold 60000 [ms] 느린 호출 지속 시간 임계값
CircuitBreaker가 느린 호출로 판단하는 임계값
permittedNumberOfCallsInHalfOpenState 10 CircuitBreaker의 상태가 반 개방(Half Open)일 때, 허용되는 호출 수 설정값
maxWaitDurationInHalfOpenState 0 [ms] CircuitBreaker가 개방(Open)으로 전환되기 전에 반 개방(Half Open)상태를 유지할 수 있는 최대 시간을 제어하는 ​​최대 대기 시간을 설정
(값 0은 허용된 모든 호출이 완료될 때까지 회로 차단기가 반 개방(Half Open) 상태에서 무한히 대기함을 의미)
slidingWindowType COUNT_BASED CircuitBreaker가 닫힐 때 호출 결과를 기록하는 데 사용되는 슬라이딩 창의 유형을 설정
(슬라이딩 윈도우는 카운트 기반(COUNT_BASED)/시간 기반(TIME_BASED) 설정 가능)

슬라이딩 윈도우가 COUNT_BASED이면 마지막 호출이 slidingWindowSize에 기록되고 집계됩니다.
슬라이딩 윈도우가 TIME_BASED이면 마지막 초의 호출이 slidingWindowSize에 기록되고 집계됩니다.
slidingWindowSize 100 CircuitBreaker가 닫힐 때 호출 결과를 기록하는 데 사용되는 슬라이딩 창의 크기를 설정
minimumNumberOfCalls 100 CircuitBreaker가 실패율 또는 느린 호출율을 계산하기 전에 필요한 최소 호출 수(슬라이딩 윈도우 기간별)를 설정

예를 들어 minimumNumberOfCalls가 10이면 실패율을 계산하기 전에 최소 10개의 호출을 기록해야 합니다.
9개의 호출만 기록된 경우 9개의 호출이 모두 실패하더라도 CircuitBreaker의 상태는 개방(Open)으로 전환되지 않습니다.
waitDurationInOpenState 60000 [ms] 개방(Open)에서 반 개방(Half Open) 상태로 전환하기 전에 CircuitBreaker가 기다려야 하는 시간 설정
automaticTransitionFromOpenToHalfOpenEnabled false true로 설정하면 CircuitBreaker가 자동으로 개방(Open) 상태에서 반 개방(Half Open) 상태로 전환되고 전환을 트리거하는 데 호출이 필요하지 않음을 의미합니다. 
waitDurationInOpenState가 통과하면 CircuitBreakers의 모든 인스턴스를 모니터링하여 HALF_OPEN으로 전환하는 스레드가 생성됩니다. 

반면 false로 설정하면 waitDurationInOpenState가 전달된 후에도 호출이 이루어진 경우에만 HALF_OPEN으로의 전환이 발생합니다.
여기서의 장점은 모든 CircuitBreakers의 상태를 모니터링하는 스레드가 없다는 것입니다.
recordExceptions empty 실패로 기록되어 실패율을 높이는 예외 목록 설정
ignoreExceptions를 통해 명시적으로 무시하지 않는 한 목록 중 하나에서 일치하거나 상속하는 모든 예외는 실패로 간주됩니다. 
예외 목록을 지정하면 ignoreExceptions에서 명시적으로 무시하지 않는 한 다른 모든 예외는 성공으로 간주됩니다.
ignoreExceptions empty 실패나 성공으로 간주되지 않는 무시할 예외 목록 설정
목록 중 하나에서 일치하거나 상속되는 모든 예외는 예외가 recordExceptions의 일부인 경우에도 실패 또는 성공으로 간주되지 않습니다.
recordFailurePredicate throwable -> true

By default all exceptions are recored as failures.
예외를 실패로 기록해야 하는지 여부를 평가하는 사용자 정의 술어(Predicate)입니다.
예외가 실패로 간주되어야 하는 경우 술어(Predicate)는 true를 리턴해야 합니다.
예외가 ignoreExceptions에 의해 명시적으로 무시되지 않는 한 예외가 성공으로 간주되어야 하는 경우 조건자는 false를 반환해야 합니다.
ignoreExceptionPredicate throwable -> false

By default no exception is ignored.
예외를 무시하고 실패나 성공으로 간주하지 않아야 하는지 평가하는 사용자 정의 술어(Predicate)입니다.
예외를 무시해야 하는 경우 술어(Predicate)는 true를 리턴해야 합니다.
예외가 실패로 간주되어야 하는 경우 술어는 false를 리턴해야 합니다.

 

 

refer.
https://resilience4j.readme.io/docs/circuitbreaker
https://resilience4j.readme.io/docs/getting-started-3