1. 서킷 브레이커(Circuit Breaker)란?
서킷 브레이커(Circuit Breaker) 패턴은 MSA 환경이나 분산 시스템에서 장애를 격리하고 시스템의 신뢰성을 높이기 위해 사용되는 디자인 패턴입니다. 전기 회로 차단기와 유사한 개념으로, 특정 조건에서 서비스의 호출을 차단해 장애 전파를 방지하는 역할을 합니다. 그렇다면 이 서킷 브레이커가 필요한 이유에 대해 자세히 알아보도록 하겠습니다.
1-1 서킷 브레이커(Circuit Breaker)가 필요한 이유
마이크로서비스 환경에서는 각 서비스가 네트워크를 통해 통신하게 됩니다. 하지만 네트워크 장애 의존 서비스의 다운타임, 과부화 등의 이유로 일부 서비스가 정상적으로 응답하지 않을 수 있습니다. 또한 문제가 발생한 서비스에 계속 요청을 보낸다면 다음과 같은 문제가 발생할 수 있습니다.
위와 같이 예매 서비스가 티켓 서비스를 호출하는 구조에서
티켓 서비스나, 네트워크 장애가 일어났을 경우에는 항상 서버 에러 응답을 받게되고
네트워크 문제나, 티켓서비스의 과부화로 인해 응답이 지연된다면 예매서비스의 쓰레드는 응답을 받기 위해 대기하게 되면서 쓰레드 고갈의 문제가 발생할 수 있습니다.
이로 인해 티켓 서비스의 장애가 -> 예매서비스까지 전파되는 문제가 발생하게됩니다.
또한 간신히 살아있는 서버에 응답시간이 지연되는 상황에서 예매 서비스의 재시도 통신이 계속해서 이뤄진다면... 간신히 살아있던 티켓 서비스의 애플리케이션은 저 멀리 요단강을 건너갈 수도 있습니다.. 이 뿐만 아니라 이러한 장애의 전파는 다른 연관된 서비스에도 전파될 위험이커지게 됩니다.
이러한 문제들을 해결하기 위해 서킷 브레이커 패턴이 등장하게 되었습니다.
1-2 서킷 브레이커(Circuit Breaker)가 동작하는 원리
서킷 브레이커는 다른 서비스에 대한 호출이 설정한 임계치를 넘어가면, 장애가 발생한 서비스로의 요청을 차단하는 역할을 하게됩니다. 서킷 브레이커는 아래와 같은 3가지의 상태를 가지고 동작하게됩니다.
Closed 상태(정상적인 상황)
요청의 실패율이 설정한 임계값 보다 낮은 상태로 서킷 브레이커가 Closed 상태라면 요청이 정상적으로 처리됩니다.
Open 상태(서킷 브레이커가 열린 상황)
요청의 실패율이 설정한 임계값을 초과한 상황으로 서킷 브레이커가 Open 상태라면 요청을 보내지 않고 실패 처리됩니다.
Half Open 상태
Open 상태에서 일정 시간이 지난 후 Half Open상태가 되는데 이 떄 요청이 들어올 경우 성공한다면 Closed 상태로 변경되고 요청이 실패한다면 Open 상태로 돌아갑니다.
+ 임계값
장애 발생 판단 기준에는 2가지가 있는데, 각 정해진 임계치가 넘을 경우 요청이 차단되게 됩니다.
slow call : 정해진 시간보다 오래 걸린 요청
failure call: 실패하거나 오류를 응답받은 요청
이러한 상태 전환을 통해 서킷 브레이커는 장애가 발생한 서비스에 대한 불필요한 요청을 방지하고, 서비스가 정상적으로 복구될 때 까지 기다릴 수 있도록 도와줍니다.
2. Resilence4J 활용한 서킷 브레이커(Circuit Breaker) 적용하기
Resilence4J에서는 서킷 브레이커에서 사용되는 정보를 슬라이딩 윈도우 방식을 사용해 호출 결과를 저장하고 집계합니다.
이 집계 방식에는 아래와 같이 2가지 방식이 있습니다.
- 카운트 기반 슬라이딩 윈도우 : 마지막 N개의 호출 결과를 집계한다.
- 시간 기반 슬라이딩 윈도우 : 마지막 N초 호출 결과를 집계한다.
이 집계 정보를 통해 서킷 브레이커 상태를 업데이트하고 요청을 차단하거나 허용하는 역할을 하게 됩니다.
지금부터는 스프링에서 주로 사용되는 Resilence4J사용해 서킷 브레이커 패턴을 구현해보도록 하겠습니다.
2-1 Resilence4J 의존성 추가
Resilence4J의 의존성을 추가해줍니다.
implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j'
2-2 application.yml 설정 추가하기
resilience4j:
circuitbreaker:
configs:
default:
slidingWindowType: COUNT_BASED
slidingWindowSize: 20
failureRateThreshold: 50
slowCallRateThreshold: 80
slowCallDurationThreshold: 10s
minimumNumberOfCalls: 10
permittedNumberOfCallsInHalfOpenState: 3
maxWaitDurationInHalfOpenState: 0
waitDurationInOpenState: 10s
instances:
reservation-service-circuit-breaker:
base-config: default
- slidingWindowType : 집계에 사용되는 슬라이딩 윈도우를 설정
- 카운트 기반 슬라이딩 윈도우와, 시간 기반 슬라이딩 윈도우가 존재 Default 값은 카운트 기반으로 작동합니다.
- slidingWindowSize : 호출 결과를 기록하는데 사용되는 슬라이딩 윈도우의 크기를 설정
- failureRateThreshold : 실패한 요청의 임계값을 백분율로 구성
- 해당 설정 값과 같거나 클 때 Open 상태로 전환되고 요청을 차단합니다.
- slowCallRateThreshold : 지연된 요청의 임계치를 설정
- 해당 설정 값과 같거나 클 때 Open 상태로 전환되고 요청을 차단합니다.
- slowCallDurationThreshold : 지연된 요청의 기준 시간 설정
- ex) 4초로 지정하면 4초 이상 걸린 요청은 지연 요청으로 집계하게 됩니다.
- minimumNumberOfCalls: 오류나 실패율을 계산하기 전 필요한 최소 호출 수
- ex) 10으로 설정한 경우 최소 10개의 호출이 기록되어야 한다 9개만 기록된 경우 9개 모두 실패해도 Open상태가 되지 않습니다.
- permittedNumberOfCallsInHalfOpenState : Half Open 상태에서 몇개 요청을 통해 상태 전환을 판단할지 설정
- ex) 3으로 설정했을 경우 3개의 요청이 모두 성공하면 Closed 상태로 변경 1개라도 실패하면 Open 상태로 변경됩니다.
- maxWailtDurationInHalfOpenState: Open상태로 전환되기 전 Half-Open 상태에 머무를 수 있는 기간
- ex) 0의 경우에는 permitted 에서 설정한 요청이 들어오기 전 까지는 Half-Open 상태로, 대기 값을 설정했을 경우에는 대기시간까지 permitted 에서 설정한 값까지 다다르지 못하면 Open상태로 변경됩니다.
- waitDurationInOpenState : Open 상태에서 Half-Open 상태로 전환되기 전 기다려야하는 시간
- ex) 10초로 설정하면 10초 후 Half-Open 상태로 변경됩니다.
2-3 OpenFeign에 CircuitBreaker 설정하기
@CircuitBreaker(name = "reservation-service-circuit-breaker")
@FeignClient(name = "ticket-service", path = "/tickets/details")
public interface TicketFeignClient {
@GetMapping
List<TicketInfoFeignResponse> getReservationsWithSeatsByTicketAndSchedule(@RequestParam("ticketId") final Long ticketId, @RequestParam("scheduleId") final Long scheduleId, @RequestParam("seatIds") final List<Long> seatIds);
}
3. 서킷 브레이커 동작 확인하기
이번엔 서킷 브레이커가 제대로 동작하는지 실제 요청을 보내 확인해보겠습니다.
(CircuitBreaker의 상태를 확인하려면 {ip}/actuator/circuitbreakers의 경로로 요청을 보내면 됩니다.)
티켓 서비스의 서버가 다운되었다는 가정하에 서버를 실행하지 않고 요청을 10회 보낸 경우
설정 임계치를 초과했을 경우 서킷브레이커의 상태가 변경된 것을 확인할 수 있습니다.
이 상태에서 10초 후 서킷브레이커의 상태를 확인하면..!
서킷 브레이커의 상태가 HALF_OPEN 상태로 변경된 것을 확인할 수 있습니다.
이번엔 티켓 서비스의 로그를 통해 실제로 OPEN 상태인 경우 요청을 보내지 않는지 확인해보도록 하겠습니다.
티켓 서비스의 API에 로그를 추가해주고 임시로 Exception을 발생하도록 설정해주었습니다.
@GetMapping("/details")
public ResponseEntity<List<TicketInfoFeignResponse>> getReservationsWithSeatsByTicketAndSchedule(
@RequestParam final Long ticketId,
@RequestParam final Long scheduleId,
@RequestParam final List<Long> seatIds) {
log.info("요청이 오는지 확인해봅시다...");
throw new RuntimeException("서킷 브레이커 테스트");
//return ResponseEntity.ok(ticketService.getReservationsWithSeatsByTicketAndSchedule(ticketId, scheduleId, seatIds));
}
이 후 다시 한 번 10번의 요청을 보내 서킷 브레이커의 상태를 OPEN 상태로 변경시켜줍니다.
그리고 계속해서 요청을 보낸 후 티켓서비스의 로그를 확인해보면 첫 10회의 요청의 로그만 남아 있고 이 후에는 티켓 서비스에 요청이 오지 않은 것을 확인할 수 있습니다.
이 때 주의할 점은 서킷 브레이커의 OPEN 상태에서 요청이 발생하면 CallNotPermittedException이 발생하게 되는데 각 예외처리 방법에 맞게 CallNotPermittedException를 처리해 주어야 합니다.
지금까지의 서킷 브레이커 동작의 흐름을 다시 한 번 정리하면 다음과 같습니다.
- 서비스 간 통신을 진행한다.
- 설정해 둔 임계값을 넘으면 서킷 브레이커 상태가 OPEN 상태로 변경된다. (이 상태에서는 요청이 차단됨)
- 대기 시간이 지나면 HALF_OPEN 상태로 변경된다.
- 설정해둔 값(permittedNumberOfCallsInHalfOpenState)만큼의 요청이 성공하면 ClOSED상태로 실패하면 다시 OPEN 상태로 변경한다.

참고
https://resilience4j.readme.io/docs/circuitbreaker
CircuitBreaker
Getting started with resilience4j-circuitbreaker
resilience4j.readme.io
https://mangkyu.tistory.com/289
[Spring] OpenFeign에 Resilence4J 서킷 브레이커 적용하는 방법과 예시 및 주의사항
이번에는 Java 진영의 서킷브레이커 라이브러리인 Resilence4J를 OpenFeign에 적용하는 방법에 대해 알아보도록 하겠습니다. 아래의 내용은 공식 문서와 직접 구현 및 테스트한 부분을 바탕으로 작성
mangkyu.tistory.com
'Backend > MSA 전환' 카테고리의 다른 글
[MSA 전환하기 9편] Transactional Outbox Pattern을 사용해 분산시스템에서 메시지 발행 신뢰성 보장하기 (0) | 2025.02.17 |
---|---|
[MSA 전환하기 8편] Kafka를 활용한 이벤트 기반 아키텍처 구축하기 (0) | 2025.02.12 |
[MSA 전환하기 6편] OpenFeign을 활용한 서비스 간 통신하기 (0) | 2025.01.29 |
[MSA 전환하기 5편] Spring Cloud Config 도입하기 (+ Spring Cloud Bus) (0) | 2025.01.20 |
[MSA 전환하기 4편] Spring Cloud Gateway 구현하기 (API Gateway) (0) | 2025.01.15 |