Decorator 패턴을 API 표면에 그대로 노출한 장애 대응 라이브러리.
Supplier<Order> decorated = Decorators.ofSupplier(() -> client.fetch(id))
.withBulkhead(Bulkhead.ofDefaults("order"))
.withCircuitBreaker(CircuitBreaker.ofDefaults("order"))
.withRetry(Retry.ofDefaults("order"))
.decorate();
Order order = decorated.get();
문서화된 기본 중첩 순서
Retry ( CircuitBreaker ( RateLimiter ( TimeLimiter ( Bulkhead ( 실제 호출 ) ) ) ) )
Retry 가 가장 바깥, Bulkhead 가 가장 안쪽
이 순서가 장애를 가른다
- Retry(CircuitBreaker(호출)) — ← 기본값
- 서킷이 열려 있으면 즉시 실패 → 재시도도 즉시 실패
- 재시도가 죽어 가는 서버를 다시 때리지 않는다 ✅
CircuitBreaker(Retry(호출))
- 재시도 3번을 '한 번의 호출' 로 묶어서 서킷이 본다
- 서킷 입장에서 실패 1건, 실제 트래픽은 3배
- 장애 서버에 부하를 3배로 실어 나른다 ❌
- 서킷이 열리기까지도 3배 오래 걸린다
순서는 resilience4j.retry.retryAspectOrder 같은 속성으로 바꿀 수 있다(값이 클수록 우선). 바꿀 수 있다는 것은 잘못 바꿀 수도 있다는 뜻이다 — 기본값을 건드릴 이유가 없으면 그대로 둔다.
각 데코레이터가 막는 것
- CircuitBreaker — 실패가 임계치를 넘으면 호출 자체를 차단한다
- 죽은 서버를 계속 때려 장애를 전파하는 것을 막는다
- Retry — 일시적 실패를 흡수한다 (반드시 백오프와 함께)
- RateLimiter — 내가 보내는 속도를 제한한다
- TimeLimiter — 응답이 없을 때 무한정 기다리지 않는다
- Bulkhead — 동시 실행 수를 제한한다
- 한 외부 연동의 지연이 전체 스레드를 잠식하는 것을 막는다
쌓을 때의 실무 규칙
- 바깥쪽에 둘 것 — 재시도 · 타임아웃 · 서킷 (호출 전체를 감싸야 의미가 있다)
- 안쪽에 둘 것 — 동시 실행 제한 · 커넥션 확보 (자원에 가까울수록 정확하다)
- 가장 바깥 — 로깅 · 메트릭
메트릭을 안쪽에 두면 재시도 3번이 3건으로 집계된다 바깥에 두면 1건으로 집계된다 — 무엇을 세고 싶은지에 따라 고른다
서킷 브레이커의 세 상태
-
CLOSED — 정상. 호출을 통과시키며 실패율을 집계한다
- 실패율이 임계치를 넘으면 → OPEN
-
OPEN — 차단. 호출을 즉시 실패시킨다 (대상 서버를 아예 안 때린다)
- 대기 시간이 지나면 → HALF_OPEN
-
HALF_OPEN — 탐색. 정해진 수만큼만 통과시켜 본다
- 성공하면 → CLOSED, 실패하면 → OPEN
핵심 값
slidingWindowSize 집계 창 크기
failureRateThreshold 열리는 실패율 (기본 50%)
waitDurationInOpenState OPEN 유지 시간
permittedNumberOfCallsInHalfOpenState 탐색 호출 수
무엇을 실패로 셀 것인가
기본적으로 모든 예외를 실패로 센다 — 이것이 흔한 함정이다
404 Not Found 나 400 Bad Request 가 예외로 올라오면 '서버는 멀쩡한데' 서킷이 열린다
- recordExceptions / ignoreExceptions 로 구분한다
- 비즈니스 예외는 무시하고, 타임아웃·5xx·연결 실패만 센다
폴백이 없으면 서킷은 반쪽이다
서킷이 열리면 호출이 즉시 실패한다 — 그다음이 정의돼 있어야 한다
- 캐시된 이전 값을 준다
- 기본값·빈 목록을 준다 (기능 일부만 죽는다)
- 사용자에게 명확히 알린다
폴백 없이 서킷만 켜면 '빨리 실패하는 장애' 가 될 뿐이다
면접 함정
- ❌ "재시도를 넣으면 안정적이다" → 백오프 없는 재시도는 장애를 증폭시킨다.
- ❌ "서킷 브레이커만 있으면 된다" → 동시 실행 제한이 없으면 한 연동이 스레드를 전부 잠식한다.