외부 서비스(의존성) 호출의 실패율을 감시하여, 실패율이 임계치를 초과하면 연동을 즉시 차단(OPEN)함으로써 시스템 전체로 장애가 전파되는 것을 차단하고 빠른 실패(Fail-Fast)를 유도하는 리질리언스 패턴.
Standard (공식 정의)
서킷 브레이커는 크게 세 가지 상태를 가지며, 유한 상태 머신(FSM)으로 동작한다.
CLOSED: 정상 상태. 모든 요청을 외부 서비스로 통과시킨다. 최근 N개 호출(Count-Based) 또는 T초간 호출(Time-Based)의 실패율을 측정한다.
OPEN: 차단 상태. 외부 서비스로 요청을 보내지 않고 즉시 예외(CallNotPermittedException)를 던져 빠른 실패를 유도한다. 특정 대기 시간(Wait Duration)이 지나면 HALF_OPEN 상태로 전이한다.
HALF_OPEN: 감시 통과 상태. 설정된 횟수만큼 제한된 요청을 외부로 전송하여 성공 여부를 측정한다. 만약 재발한 실패율이 임계치 이하면 CLOSED로 복귀하고, 또다시 임계치를 초과하면 OPEN으로 회귀한다.
한계 / 주의점
지표 누수(Metric Cardinality Explosion): Resilience4j 등 라이브러리는 기본적으로 매우 세부적인 게이지와 카운터 지표(예: slow call rate, buffered calls 등)를 대량 방출한다. 이를 모니터링 시스템(Prometheus 등)에 그대로 전송하면 시계열 데이터 개수가 급증하여 저장소 과부하를 초래한다. 실무에서는 엄격히 합의된 저카디널리티(low-cardinality) 필수 지표만 필터링하여 통과시켜야 한다.
Retry와의 충돌: 서킷 브레이커와 리트라이를 무작정 함께 배치하면, 하나의 외부 요청 실패가 리트라이 3회로 증폭되어 서킷 브레이커가 오작동하거나 윈도우 슬라이딩의 실패율이 왜곡될 수 있다.
OutboundHttpResilience에서 각 의존성별로 독립된 CircuitBreaker와 Retry를 구성함.
OutboundHttpResilienceConfig에서 D3/D4 가이드라인을 강제하여:
리질리언스를 켤 때 지표 수집기(MeterRegistry)가 없으면 애플리케이션 기동을 에러로 즉시 차단(Activation Guard).
Prometheus 지표 수집을 위해 resilience4j.retry.calls, resilience4j.circuitbreaker.calls, resilience4j.circuitbreaker.state 딱 3가지 필수 지표만 허용하고 나머지는 강제 차단(Deny Filter)함.
가시성을 높이기 위해 벤더 사양의 태그를 outcome (SUCCESS/FAILURE) 및 대문자 state (CLOSED, OPEN, HALF_OPEN)로 정형화(Metric Normalisation)하여 바인딩함.
왜 리트라이와 서킷 브레이커를 결합할 때 데코레이팅 순서가 중요한가? (CB가 Retry의 바깥쪽에 위치해야 각 재시도 실패가 개별적으로 서킷 실패율에 반영되지 않고 전체 실패로 깔끔하게 묶이거나, 혹은 구조에 따라 왜곡이 발생할 수 있음을 알아야 한다.)
카디널리티 폭발(Metric Cardinality Explosion)이란 무엇이며, 우리 프로젝트는 이를 어떻게 대처했는가?
Interview Questions
마이크로서비스 환경에서 서킷 브레이커의 필요성과 작동 방식(FSM)을 설명하십시오.
서킷 브레이커를 적용한 후 모니터링 시스템의 시계열 부하(Cardinality)가 급증하는 문제를 해결하기 위해 구체적으로 어떤 조치를 취할 수 있습니까?
Do Not Overclaim
"서킷 브레이커가 동작하면 분산 시스템의 네트워크 순단에 대비해 무조건 가용성이 높아진다"고 단정하면 안 된다. 서킷이 열려 있는(OPEN) 동안은 정상 요청조차 즉시 거절되므로, 가용성은 일시적으로 0이 된다. 서킷 브레이커의 목표는 가용성 향상뿐 아니라 호출 측의 스레드 고갈 방지 및 업스트림 서버 보호임을 명시해야 한다.