Spring

Spring boot CircuitBreaker 간단하게 알아보기

jay Joon 2023. 2. 18. 01:09

- 개요 -

스프링 프로젝트에서 서킷브레이커를 적용하기 위해서는 아래의 2가지 프레임워크를 이용합니다

  • hystrix circuit breaker
  • resilience4j circuit breaker

현재는 hystrix circuit breaker는 2021년 11월 1일부로 공식적인 지원이 중단되어
사용을 추천하지 않습니다. 😭

 

따라서 이번 글에서는 resilience4j 의 circuit breaker을 설명하도록 하겠습니다.

 

우선 의존성은 아래와 같습니다.

dependencies {
    implementation 'io.github.resilience4j:resilience4j-spring-boot2:{version}'
}

스프링 부트 버전과 호환되는 각각의 resilience4j 은 아래와 같으니 현재 자신의 프로젝트에 맞는
version을 사용해 주세요!

  • Spring Boot 2.6.x: Resilience4j-Spring Boot 1.7.x
  • Spring Boot 2.5.x: Resilience4j-Spring Boot 1.6.x
  • Spring Boot 2.4.x: Resilience4j-Spring Boot 1.5.x
  • Spring Boot 2.3.x: Resilience4j-Spring Boot 1.4.x
  • Spring Boot 2.2.x: Resilience4j-Spring Boot 1.3.x

 

- 본문 - 

설정

우선 CircuitBreaker를 사용하기 위해선 config 설정을 먼저 하여야 하는데 두 가지 방법이 존재합니다.

  • 직접 Config 객체를 만들어 설정하는 법
  • yml 파일에 명시하여 설정하는 법 (이번글을 읽고 공식문서만 잠깐 참고하여도 이해하는데 크게 문제가 없음으로 이번 글에서는 설명하지 않겠습니다.)

직접 Config 객체를 만들어 설정하는 법은 아래와 같습니다!

CircuitBreakerConfig config = CircuitBreakerConfig.custom() 
                .failureRateThreshold(50)
                .waitDurationInOpenState(Duration.ofMillis(1000))
                .permittedNumberOfCallsInHalfOpenState(3) 
                .slidingWindowSize(100) 
                .build();

각각의 설정의 의미는

  • failureRateThreshold(50) : Circuit Breaker가 Open 상태로 전환될 실패율 임계값을 50%로 설정 → slidingWindowSize 개의 실패한 호출의 비율이 50%를 초과하면 Circuit Breaker가 Open 상태로 변경 (100개 중 50개가 실패면 open)
  • waitDurationInOpenState(Duration.ofMillis(1000)) : Circuit Breaker가 Open 상태에서 Half-Open 상태로 전환될 때까지 대기하는 시간을 1초로 설정
  • permittedNumberOfCallsInHalfOpenState(3) : Circuit Breaker가 Half-Open 상태에서 허용되는 최대 호출 횟수를 3으로 설정. Half-Open 상태에서 3번 호출 시 모두 성공해야 다시 closed 상태로 변한다. (한 번이라도 실패한 경우 다시 open)
  • slidingWindowSize(100) : Circuit Breaker가 Closed 상태일 때 통계를 수집하기 위한 window size를 100으로 설정

이렇게 config 설정을 완료하였다면 CircuitBreaker를 아래와 같이 생성하면 됩니다!

CircuitBreaker circuitBreaker = CircuitBreaker.of("testCircuitBreaker", config);

여기까지 했다면 CircuitBreaker(testCircuitBreaker)가 정상적으로 생성되었습니다.

 

다음은 생성된 CircuitBreaker을 사용해 봅시다.

circuitBreaker.decorateCheckedSupplier(() -> {
            System.out.println("실행할 로직");
            return "test";
        });

간단하게는 다음과 같으며 decorateCheckedSupplier 이외에도 다른 decorater 메서드들이 많으니 필요에 따라 사용하면 됩니다.

 

만약 실행할 로직에서 예외가 발생한 경우 fallback 코드도 아래와 같이 설정할 수 있습니다.

CheckedFunction0<String> checkedSupplier = circuitBreaker.decorateCheckedSupplier(() -> {
            throw new RuntimeException("무조건 실패");
        });

String result = Try.of(checkedSupplier).recover(throwable -> {
            log.error("실행 로직 실패 fallback 동작  error: {}", throwable.getMessage());
            return "fallback";
        }).get();

System.out.println(result);

위 코드를 실행 시 다음과 같은 결과를 얻게 됩니다.

즉 error 가 발생하여도 실패에 따른 동작을 지정할 수 있습니다.

 

이렇게 error 가 발생하면 앞서 설정한 config 설정대로 집계하다가 실패율 임계치가 넘는 순간 서킷의 상태는 OPEN으로 변경되고 본 로직을 실행하지 않고 바로 fallback 로직을 실행하게 됩니다.

 

코드로 보면 다음과 같습니다.

circuitBreaker.transitionToOpenState(); // 강제로 open 상태로 변경

CheckedFunction0<String> checkedSupplier = circuitBreaker.decorateCheckedSupplier(() -> {
    throw new RuntimeException("무조건 실패");
});

String result = Try.of(checkedSupplier).recover(throwable -> {
    log.error("실행 로직 실패 fallback 동작  error: {}", throwable.getMessage());
    return "fallback";
}).get();
  
System.out.println("result :" + result);

결과

 

 

조금 더 알아보기

이렇게 간단하게 서킷브레이커를 사용하는 방법을 알아보았는데요.

추가적으로 애매한 부분을 더 알아봅시다.

만약 아래처럼 동일한 서킷브레이커 이름으로 두 번 생성 시 어떻게 될까요?

CircuitBreaker circuitBreaker1 = CircuitBreaker.of("testCircuitBreaker", config);
CircuitBreaker circuitBreaker2 = CircuitBreaker.of("testCircuitBreaker", config);

circuitBreaker1과 circuitBreaker2 은 동일한 객체일까요?

 

맞습니다 동일한 객체입니다. 서킷브레이커 등록 시 testCircuitBreaker에 해당하는 이름이 이미 존재할 시 같은 인스턴스를 반환하게 됩니다. ( circuitBreaker1 == circuitBreaker2 )

 

만약 다른 로직에서 생성된 서킷 브레이커(testCircuitBreaker)를 가져오고 싶을 땐 아래와 같이 가져오면 됩니다.

CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults();
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testCircuitBreaker");

 

 

마무리

이렇게 간단하게 서킷브레이커에 대해서 알아보았는데요.

해당 글에서 설명한 내용은 서킷브레이커 설정의 일부분 임으로 추가적인 내용이 필요하시다면

  • 특정 에러만 실패 집계
  • 애노테이션 기반의 서킷브레이커 구성
  • yml 설정 (문서)

등 다양한 설정이 필요하시다면 공식문서를 참고하시면 도움이 될 것 같습니다.

 

감사합니다.