서킷 브레이커는 서킷이 열려 있을 때 빠른 실패 로직을 수행한다. 즉 이어지는 호출에서 시간 초과, 예외 발생 등 오류가 발생하지 않게, 폴백 메서드 호출을 리다디렉션을 한다. 폴백 메서드에서 다양한 비즈니스 로직을 적용하면 로컬 캐시의 데이터를 반환하거나 즉각적인 오류 메시지를 반환하는 등 최적화된 응답을 생성할 수 있다. 이로써 의존하는 서비스의 응답 중단 때문에 마이크로 서비스가 응답하지 못하게 되는 문제를 방지할 수 있는다.
시간이 지나면 서킷 브레이커는 반열림 상태로 전환돼 새로운 호출을 허용하며, 이를 통해 문제를 일으킨 원인이 사라졌는지 확인한다. 서킷 브레이커는 새로운 오류를 감지하면서 서킷을 다시 열고 빠른 실패 로직을 다시 수행하며, 오류가 사라졌으면 서킷을 닫고 정상 작동 상태로 돌아간다.
Resilience4j는 런타임에 다양한 방법으로 서킷 브레이커의 정보를 공개한다.
서킷 브레이커의 현재 상태를 마이크로 서비스 액추에이터 상태 점검 엔드 포인트(/actuator/health)를 사용해 모니터링할 수 있다.
서킷 브레이커는 상태 전이 등의 이벤트 액추에이터 엔드 포인트(/actuator/citcuitbreakerevents)를 게시한다.
서킷 브레이커 스프링 부트의 매트릭스 시스템과 통합돼 있으며 이를 이용해 프로테우스와 같은 모니터링 도구에 메트릭을 게시할 수 있다.
반열림 상태에서의 호출 수로, 서킷을 다시 열거나 닫힘 상태로 돌아갈지를 결정할 때 사용 한다.
automaticTransitionFromOpenToHalfOpenEnabled
대기 시간이 지난 후에 서킷을 반열림 상태로 자동 전환할지, 첫 번째 호출이 들어오길 기다렸다가 반열림 상태로 전환할지를 결정한다.
ignoreExceptions
오류를 간주하지 않을 예외를 지정한다. 보통 InvaildInputException, NotfoundExceotion과 같이 예상할 수 있는 비즈니스 예외는 서킷 브레이커에서 무시하는데, 존재하지 않은 데이터 검색이나 유요하지 않은 입력으로 발생하는 예외 때문에 서킷을 열어선 안되기 때문이다.
다음과 같이 사용할 수 있다.
ringBufferSizeInClosedState = 5, failureRateThreshold = 50% : 마지막 5개의 호출 중 3개 이상이 실패하면 서킷이 열린다.
waitInterval = 10000, automaticTransitionFromOpenToHalfOpenEnabled = true : 서킷 브레이커는 10초 동안 서킷을 열린 상태로 유지했다가 반열림 상태로 전환한다.
ringBufferSizeInHalfOpenState = 3 : 서킷 브레이커는 서킷 반열림 상태로 전환 후에 처음으로 유입된 3개의 호출을 기준 삼아 서킷의 열림 여부를 결정한다. failureRateThreshold = 50%으로 설정돼 있기 때문에 2개 이상 호출이 실패하면 서킷이 다시 열리며, 이 외 경우에는 서킷이 닫힌다.
ignoreExceptions = InValidInputException, NotFoundException : 이 두 가지 비즈니스 예외는 서킷 브레이커에서 오류 여기지 않는다.
재시도 메커니즘은 일시적인 네티워크 결함과 같음 무작위로 드물게 발생하는 오류에 매우 유용하다. 재시도 메커니즘은 설정된 대기 시간을 사이에 두고, 실패한 요청에 여러번 다시 시도하는 것이다. 재시도 메커니즘을 사용하기 위한 주요 요건 중 하나는 재시도 대상 서비스의 멱등성이 있어야 한다는 것이다. 만약 재시도 메커니즘에 의해 2개의 주문이 생성되는 일이 발생하지 않아야 하기 때문이다.
Resilience4j는 서킷 브레이커와 같은 방식으로 재시도 관련된 이벤트 및 메트릭 정보를 공개하지만 상태 정보는 전혀 공개하지 않으며, 재시도 이벤트에 관한 정보는 Actuator 엔드 포인트에서 얻을 수 있다. Resilience4j는 다음과 같은 매개변수를 사용한다.
Properties
Description
maxRetryAttempts
첫번째 호출을 포함한 총 재시도 횟수
waitDuration
재시도를 다시 수행하기 전의 대기 시간
retryExceptions
재시도를 트러거하는 예외 목록
다음과 같이 사용 가능하다.
maxRetryAttempts = 3: 최대 두 번의 재시도를 수행한다.
waitDuration = 1000: 재시도 사이의 대기 시간은 1초다.
retryExceptions = IntervalServerError:HTTP 요청에 대한 응답으로 500 상태 코드가 오고, 발생한 예외가 IntervalServerError인 경우에만 재시도를 트리거 한다.
resilience4j: circuitbreaker: configs: default: registerHealthIndicator:true# actuator 정보 노출을 위한 설정 ringBufferSizeInClosedState:10# 닫힌 상태에서의 호출 수로, 서킷을 열어야 할지 결정할 때 사용한다. ringBufferSizeInHalfOpenState:30# 반열림 상태에서의 호출 수로, 서킷을 다시 열거나 닫힘 상태로 돌아갈지를 결정할 때 사용 한다. failureRateThreshold:80# 실패한 호출에 대한 임계값(백분율)으로 이 값을 초과하면 서킷이 열린다. waitDurationInOpenState:10s# 열림 상태를 유지하는 시간, 해당 시간이후 반열림 상태로 변경된다. slidingWindowType:COUNT_BASED
instances: findOderByUserId: baseConfig:default
management: endpoints: web: exposure: include: -"*"# 테스트를 위해 actuator 전체 노출
health: circuitbreakers: enabled:true# circuitbreakers 정보 노출
설정은 10개의 ringBuffer가 80% 이상 발생하면 10s 동안 서킷을 오픈하고 반오픈 상태로 변경 변경되게 설정했습니다.
정상적인 상태입니다. 이상 태에서 /user-service/api/v1/users/997a5a8b-80e4-4a5d-b5d1-14ee22be18da/orders?faultPercentage=100&delay=0를 2번 호출해 보겠습니다. faultPercentage 100이기 때문에 2번 모두 실패하고 Fallback 메서드가 발생하게 됩니다.
bufferedCalls, failedCalls 각각 증가한 것을 확인할 수 있습니다. bufferedCalls는 호출 수, failedCalls는 실패하여 Fallback 메서드가 내려간 수입니다.
그러면 /api/v1/users/997a5a8b-80e4-4a5d-b5d1-14ee22be18da/orders?faultPercentage=0&delay=0 faultPercentage 0으로 호출하여 정상적인 응답을 받고 다시 CircuitBreaker Health를 확인해 보겠습니다.