Spring boot CircuitBreaker 간단하게 알아보기
- 개요 -
스프링 프로젝트에서 서킷브레이커를 적용하기 위해서는 아래의 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 설정 (문서)
등 다양한 설정이 필요하시다면 공식문서를 참고하시면 도움이 될 것 같습니다.
감사합니다.