서킷브레이커 패턴

od·2025년 4월 8일
0

System Design

목록 보기
5/8

서킷브레이커 패턴(Circuit Breaker Pattern)은 외부 서비스에 문제가 지속적으로 발생하는 경우 해당 서비스로의 접근을 일시적으로 차단하여시스템 전체에 연쇄적인 오류나 성능 저하가 발생하는 것을 방지하는 디자인 패턴입니다.


서킷브레이커 상태

서킷브레이커는 Closed, Open, Half Open 의 상태로 구분됩니다.


Closed 상태는 서킷 브레이커가 닫혀 있는 상태로 모든 요청이 정상적으로 외부 서비스로 전달됩니다.
이 상태에서 예외가 발생하거나 응답 시간이 초과되면 fallback 메서드가 실행되고 동시에 실패 카운트가 증가합니다.
실패 비율이 설정된 임계값을 초과하면, 서킷 브레이커는 Open 상태로 전환됩니다.

Open 상태는 서킷 브레이커가 열려 있는 상태로 외부 서비스에 대한 접근이 차단됩니다.
요청은 외부로 전달되지 않고 fallback 메서드로 우회되며 Open 상태 유지 시간이 지나면 Half Open 상태로 전환 됩니다.

Half Open 상태는 외부 서비스가 다시 정상 작동 하는지 확인하는 상태 입니다. 제한된 수의 요청만 외부 서비스로 전달한 후 요청이 성공하면 서킷 브레이커는 Closed 상태로 복구되고 실패하면 다시 Open 상태로 전환 됩니다.





서킷브레이커 구현

Resilience4j는 현재 가장 많이 사용되는 서킷 브레이커 라이브러리 중 하나 입니다.
설정 파일과 어노테이션을 통해 손쉽게 서킷 브레이커를 적용할 수 있도록 추상화되어 있으며 AOP 기반으로 동작하기 때문에 Self Invocation에 주의해서 사용해야 합니다.

implementation 'org.springframework.boot:spring-boot-starter-aop'
implementation 'io.github.resilience4j:resilience4j-spring-boot3:2.1.0'
resilience4j:
    circuitbreaker:
      configs:
        redisSetting:
          sliding-window-size: 4             
          failure-rate-threshold: 50          
          slow-call-duration-threshold: 1s     
          slow-call-rate-threshold: 25         
          wait-duration-in-open-state: 2s      
          permitted-number-of-calls-in-half-open-state: 3
      
      instances:
        redis: # 서킷브레이커 이름
          baseConfig: redisSetting
@Component
public class RedisAdapter {
    
    RestTemplate restTemplate = new RestTemplate();

    @CircuitBreaker(name = "redis", fallbackMethod = "redisFallback")
    public String findRedisCache(int index)  {
        String url = "<http://localhost:8080/redis?index=>" + index;
        return restTemplate.getForObject(url, String.class);
    }

	// 서킷브레이커 오픈시 실행되는 메소드 (타겟 메소드의 입력 파라미터가 자동으로 입력됩니다)
    public String redisFallback(int index, Exception e) { 
        System.out.println("redisFallback - " + e.getMessage());
        return null;
    }
}
keyvaluedescription
sliding-window-size4CLOSED 상태에서 테스트 횟수
failure-rate-threshold50실패율 임계값(%)
slow-call-duration-threshold1sCLOSED 상태에서 최대 응답 시간
slow-call-rate-threshold25최대 응답 시간 초과율 임계값(%)
wait-duration-in-open-state2sOPEN 상태 유지시간
permitted-number-of-calls-in-half-open-state3HALF_OPEN 상테에서 테스트 횟수

위의 설정이 적용된 서킷브레이커는 다음과 같이 작동하게 됩니다.

  1. Closed → Open 상태로 전환되는 첫 번째 케이스 (실패율 임계값에 따른 오픈)
    • 4번 호출에 50% 의 요청이 실패하면 서킷 브레이커가 오픈됩니다.
    • 요청 1 : findRedisCache() 호출 → 실패 → redisFallback() 실행 및 실패카운트 증가
    • 요청 2 : findRedisCache() 호출 → 성공
    • 요청 3 : findRedisCache() 호출 → 성공
    • 요청 4 : findRedisCache() 호출 → 실패 → redisFallback() 실행 및 실패카운트 증가
    • 4번 호출에 50% 실패 하였으므로 서킷브레이커 OPEN
  2. Closed → Open 상태로 전환되는 두 번째 케이스 (최대 응답시간 임계값에 따른 오픈)
    • 4번 호출에 25% 의 요청이 2초 이상 걸린다면 서킷브레이커가 오픈됩니다.
    • 요청 1 : findRedisCache() 호출 → 응답시간 1초
    • 요청 2 : findRedisCache() 호출 → 응답시간 1초
    • 요청 3 : findRedisCache() 호출 → 응답시간 3초
    • 요청 4 : findRedisCache() 호출 → 응답시간 1초
    • 4번 호출에 25% 의 요청이 2초 이상 걸렸으므로 서킷브레이커 OPEN
  3. 서킷 브레이커 오픈 상태가 2초간 유지됩니다.
    • 서킷브레이커가 오픈된 상태에서는 findRedisCache() 를 호출하더라도 해당 로직이 실행되지 않고 redisFallback() 이 실행됩니다.
  4. 2초 후 HALF-OPEN 상태로 전환됩니다.
    • findRedisCache() 를 세 번 시도하여 세 번 연속 실패하는 경우 OPEN 상태로 전환되며 성공하는 경우 CLOSED 상태로 전환됩니다.





서킷브레이커 관리

CircuitBreakerRegistry 는 서킷브레이커 관리에 사용되는 인터페이스 입니다.
이를 통해 서킷브레이커 실패 카운트를 변경하거나 초기화 할 수 있습니다.

@Service
@RequiredArgsConstructor
class ResetCircuitBreakerService {

    private final CircuitBreakerRegistry circuitBreakerRegistry;

	public void resetCircuitBreaker(String circuitBreakerName) {

		CircuitBreaker circuitBreaker =
			circuitBreakerRegistry.circuitBreaker(circuitBreakerName);
			
		circuitBreaker.reset(); // 실패 카운트 초기화
		circuitBreaker.onError(0, TimeUnit.MILLISECONDS, 
			new RuntimeException("manual failure")); // 실패 카운트 증가
		circuitBreaker.transitionToOpenState(); // Open
		circuitBreaker.transitionToHalfOpenState(); // Half Open
		circuitBreaker.transitionToClosedState(); // Closed
    }
}

만약 프로메테우스와 actuator 를 함께 사용하고 있다면
다음의 설정을 통해 그라파나에서 서킷브레이커 상태 모니터링이 가능합니다.

management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus
  metrics:
    enable:
      resilience4j.circuitbreaker: true
  endpoint:
    prometheus:
      enabled: true
profile
차분하게 단단히 쌓아가는 개발자

0개의 댓글