Spring cloud circuit breaker #
Circuit breaker #
- 전기 회로의 차단기와 유사한 역할을 하는 소프트웨어 디자인 패턴
- 특정 서비스가 과부하 상태가 되었을 때 추가적인 요청을 차단하여 시스템의 안정성을 유지
- 해당 서비스가 복구될 수 있는 시간 확보
- Reactive systems의 Resilient(복원력)를 지원
Spring Cloud Circuit Breaker #
- Spring에서 circuit breaker를 지원하기 위해 만든 라이브러리
- resilience4j와 spring retry를 추상화하여 Circuit breaker를 지원
- reactive 환경에서는 resilience4j 만 사용 가능
Resilience4j #
- java에서 circuit breaker를 지원하는 라이브러리
- Circuit breaker, Rate Limiter, Retry, Bulkhead, Time Limiter, Cache 등의 기능을 제공
Circuit breaker 준비 #
- org.springframework.cloud:springcloud-starter-circuitbreaker-reactorresilience4j 를 추가
- mavenBom에 cloud-dependencies 버전을 명시
- circuit breaker에는 별도의 버전 명시 없이 사용 가능
Circuit breaker 상태 #
- Circuit breaker의 상태는 finite state machine으로 표현 가능
- Closed, Open, Half Open으로 구성
- Closed: 정상적으로 요청을 받을 수 있는 상태
- Open: Circuit breaker가 작동하여, 목적지로 가는 트래픽, 요청을 막고 fallback 반환
- Half Open: 트래픽을 조금 흘려보고 Open을 유지할지 Closed로 변경할지 결정
Circuit breaker 상태 변화 #
- Closed에서 Open으로 변경
- 호출하는 대상에 문제가 생긴 경우
- Open에서 Half Open으로 변경
- 일정 시간이 지나거나
- 강제로 상태 변경
- Half Open에서 Open 혹은 Close로 변경
- half open 동안 실제 대상을 호출하여 문제가 없는지 검증
Reactor transform 연산자 #
- 원본 publisher를 인자로 전달 받고 변환된 publisher를 반환
- 통째로 넘기기 때문에 다른 연산자보다 더 유연하게 데이터 스트림을 처리 가능
Reactor transform 연산자 테스트 #
- 함수형 인터페이스를 직접 구현
- apply에서 인자로 현재 mono를 받고 각각의 item에 1을 더하는 연산자를 추가하여 반환
- error를 반환하는 Flux를 무조건 1을 반환하는 Flux로 변환하여 반환
Circuit breaker closed #
- 기본 상태
- 들어오는 모든 요청을 대상 메소드, 서비스에 전달
- 서비스에 전달 후 응답이 느리거나 error가 발생한다면 fallback을 실행하여 결과 전달
closed 테스트 서비스 #
- ReactiveCircuitBreakerFactory bean 주입
- doGreeting을 호출하여 특정 delay 이후 인사를 반환하는 Mono 획득
- transform을 사용하여 해당 publisher를 circuit breaker에 전달
- “normal” id를 갖는 circuit breaker를 생성
- fallback으로 fallbackMessage를 반환
closed 테스트 어노테이션 #
- GreetingCircuitBreakerServiceTest를 생성
- AutoConfigureReactiveCircuitBreaker를 만들어서 제공
closed 테스트 설정 #
- 로깅을 위한 customizer 추가
- 요청이 성공하거나 문제가 발생하거나
- circuit breaker의 상태가 바뀌거나
- slowCallRate, failureRate 등이 바뀌면 로깅
- eventLogger 외에는 모두 기본 설정
- configureDefault를 통해 별도의 설정을 갖지 않는 다른 모든 circuit breaker에 해당 설정이 적용
- “normal”에 대한 별도의 설정을 제공하지 않기 때문에 normal circuit breaker는 default config를 사용
closed 테스트 #
- 테스트 생성
- Greeter를 @SpyBean으로 설정
- GreetingCircuitBreakerService와 CircuitBreakerRegistry를 @Autowired로 successMessage과 fallbackMessage를 지정
success 예제
- delay를 0ms로 제공
- 바로 기대한 결과인 “Hello grizz!” 를 반환
- circuitBreaker id를 normal로 설정했기 때문에 logging에서도 normal로 남는다
- greeter의 generate는 한번 실행
GreetingCircuitBreakerServiceTest
fallback 예제
- delay를 5000ms로 제공
- withVirtualTime을 사용해서 시간이 지난 것처럼 시뮬레이션
- 1초가 지나는 시점에 “Hello world!” 반환
- 5초를 기다리게 해도 결과는 동일
- circuit breaker에서 TimeoutException 발생시키고 fallback 실행
- greeter는 중간에 cancel되어 실행되지 않음
exception이 발생한 경우도 fallback 실행
- it 이 Mono의 결과 (error)
Circuit breaker sliding window #
- circuit breaker는 대상이 되는 서비스 호출과 관련된 성공, 실패 여부를 sliding window 형태로 저장
- count-based sliding window에서는 특정 개수 n개만큼의 측정 결과를 저장
- 저장된 측정 결과의 개수가 n개에 도달하면 최초에 저장했던 결과를 제거하고 새로운 결과를 저장
- circular array를 사용
- 각각의 과정에서 전체 개수 대비 실패 비율을 계산
- 이를 failure rate 라고 부른다
sliding window와 failure rate #
- failure rate = 실패한 호출 수 / sliding window 크기
- item 하나가 새로 추가되면 failure rate 계산에 O(1) 만큼의 시간복잡도가 필요
- sliding window 크기는 고정이기 때문에 총 합에서 새로운 값을 더하고 없어진 값을 제거하여 다시 계산
- failure rate이 설정한 임계치에 도달하는 순간 closed 상태에서 open 상태로 변경
closed에서 open으로 전환 #
- closed 상태에서 open 상태로 바뀌면서 circuit breaker는 state transition 이벤트를 발행
closed 전환 테스트 #
-
circuit breaker id를 외부에서 바꿀 수 있게 추가
-
sliding window size를 4
- 기본값은 100
-
failure rate threshold를 50
- failure rate 임계치를 퍼센티지로 표현
- 기본값은 50이고 0에서 100 사이
-
“mini” 라는 이름을 갖는 circuit breaker 생성
-
sliding window size가 4이고 failure rate threshold가 50이기 때문에 전체 호출 중 절반이 실패하는 순간 open으로 전환
-
처음 4개가 성공하여 failureRate이 0.0
-
이후 2개가 실패하고 failureRate이 50.0이 되면서 open 상태로 전환
Circuit breaker open #
- open 상태에서는 기존에 호출하던 대상을 더이상 호출하지 않는다
- fallback이 실행되어 반환
- 호출하는 서비스를 보호하고 복구할 수 있는 시간 확보
- fallback이 실행되기 때문에 서비스의 장애가 전파되지 않는다
open 테스트 #
- open 상태에서는 delay를 0으로 전달해도 무조건 fallback 메세지 반환
- closed 상태였다면 무조건 성공
- 100번을 호출했지만, spyGreeter는 처음 성공했던 4번 호출 이후 한번도 호출되지 않는다
open에서 half open으로 #
- open 상태에서 half open 상태로 바뀌면서 circuit breaker는 state transition 이벤트를 발행
- open 상태에서 half open으로 가기 위해서는 2가지 방법이 존재
- circuit breaker api를 사용해서 명시적으로 변경
- 옵션을 지정하여 특정 시간이 지나면 자동으로 변경
open 전환 테스트 #
- 처음 4번 실패하게 만들어 open 상태로 전환
- CircuitBreakerRegistry를 이용해서 circuitBreaker에 접근하고 transitionToHalfOpenState를 호출
- 하지만 직접 상태를 관리해야 하기 때문에 어쩔 수 없는 겨우가 아니라면 가급적 권장 하지 않는다
open 전환 테스트 #
- enableAutomaticTransitionFromOpenToHalfOpen: 일정 시간이 지나면 자동으로 open에서 half open으로 변경되게 설정
- waitDurationInOpenState: 얼마만큼의 시간이 지난 후 half open으로 변경할지 설정
예제
- 처음 4개를 실패 상태로 만들어 open 상태로 waitDurationInOpenState를 5초로 설정했기 때문에 6초동안 대기
- 이후 자동으로 half open 상태로 변경
Circuit breaker half open #
- half open 상태에서 정해진 개수만큼 요청을 전달
- half open에서 측정한 결과에 따라서 open 혹은 closed로 변경
- 대상 서비스가 복구된 경우, 자동으로 장애 복구하기 위한 목적
half open에서 closed 혹은 open #
- half open 상태에서 sliding window와 같이 특정 개수(permittedNumberOfCalls)에 대해서 측정 결과 저장
- half open의 failure rate = 실패한 호출 수 / permittedNumberOfCalls
- open으로 전환 : failure rate이 임계점보다 높거나 같다면
- close로 전환 : failure rate이 임계점보다 낮다면
half open 전환 테스트 #
- 3초가 지나면 자동으로 open에서 half open으로 전환
- half open 상태에서 6개의 요청을 분석하고 3개 이상 실패하면 open으로 2개 이하 실패하면 closed로
- permittedNumberOfCallsInHalfOpenState가 6이라서 6개의 요청을 분석
- failureRateThreshold가 50이기 때문에 3개가 기준이 된다
close로 변경 예제
- circuit breaker를 half open 상태로 변경
- 4번은 성공, 2번은 실패
- 6번 (permittedNumberOfCalls) 요청이 끝나는 시점에 failureRate은 33퍼센트
- 50퍼센트 (failureRateThreshold) 보다 작기 때문에 closed로 상태 변경
open으로 변경 예제
- circuit breaker를 half open 상태로 변경
- 3번은 성공, 3번은 실패
- 6번 (permittedNumberOfCalls) 요청이 끝나는 시점에 failureRate은 50퍼센트
- 50퍼센트 (failureRateThreshold) 보다 크거나 같기 때문에 open으로 상태 변경
Circuit breaker 설정 #
- ReactiveResilience4JCircuitBreakerFactory에 CircuitBreakerConfig와 TimeLimiterConfig 제공
- CircuitBreakerConfig는 circuit breaker 설정 제공
- TimeLimiterConfig는 timeout과 관련된 설정 제공
CircuitBreakerConfig 설정 #
- slidingWindowSize: 호출 결과를 저장할 sliding window 크기. 기본 100
- failureRateThreshold: 실패 비율 임계점. 기본 50
- enableAutomaticTransitionFromOpenToHalfOpen: open에서 half open으로 자동 전환할지 여부. 기본 false
- waitDurationInOpenState: open에서 half open로 전환될 때까지 필요한 시간. 기본 60초
- permittedNumberOfCallsInHalfOpenState: half open 상태에서 허용할 호출 수. 기본 10
- ignoreExceptions: 서비스에서 exception을 던지는 경우, 허용할 exceptions 목록. 기본 empty
- maxWaitDurationInHalfOpenState: half open 상태에서 대기할 수 있는 최대 시간. 기본 0
TimeLimiterConfig 설정 #
- cancelRunningFuture: future가 진행중인 경우, cancel 할지 여부. 기본 true
- timeoutDuration: timeout 기준 시간. 기본 1초
- factory.configure에 circuit breaker id를 전달하여 특정 id를 갖는 circuit breaker들에 대한 설정 가능
- 일반적인 모든 circuit breaker에 설정을 적용하려면 configureDefault를 사용
Circuit breaker yaml 설정 #
- yaml을 통해서도 설정 가능
- resilience4j.circuitbreaker.instances.
로 특정 circuit breaker instance의 설정을 주입 - resilience4j.circuitbreaker.instances.example을 앞선 @Bean 설정과 동일
- resilience4j.circuitbreaker.configs.default를 설정하여 정확히 일치하는 instance가 없는 circuit breaker에 대한 설정 제공
Circuit breaker group #
- circuit breaker instance를 만들면서 group을 제공할 수 있다
- 아래의 순서로 설정을 찾게 된다
- instance id와 정확히 일치하는 설정 사용
- 없다면, group과 정확히 일치하는 설정 사용
- 없다면, default 설정
- 강의 : Spring Webflux 완전 정복 : 코루틴부터 리액티브 MSA 프로젝트까지_