[Resilience4j] Circuit Breaker With Spring Boot 2
2022. 7. 10. 22:08ㆍ개발/Spring
Resilience4j의 Circuit Breaker에 대한 개념을 정리하고 이를 Spring 프로젝트에 적용해본다.
1.기본 개념
1.1 Circuit breaker State
- 서킷 브레이커는
CLOSED, OPEN, HALF_OPEN
3가지의 상태를 가진다CLOSED
는 요청 집계값이 threshold 미만인 상태를 유지하며 정상적으로 요청이 가능한 상태.OPEN
은 요청 집계값이 threshold를 초과해서 서비스간 요청이 불가능한 상태.HALF_OPEN
는OPEN
상태에서 특정 대기시간이 경과하면 발생하는 상태이며, 추가적인 요청 집계값이 threshold 보다 낮으면CLOSED
로 전환되고, 반대의 경우 다시OPEN
으로 복귀된다.
- 여기에 2개의 특수한 상태인
DISABLED, FORCED_OPEN
이 있다. - 서킷 브레이커는 Sliding window를 통해 요청에 대한 측정값을 집계한다.
- count-based 방식과 time-based 방식을 제공한다.
- count-based 방식은 가장 최근 N번의 요청 횟수를 집계해서 상태변경에 참고한다.
- time-based 방식은 가장 최근 N초의 요청 횟수를 집계해서 상태변경에 참고한다
1.2 Count-based sliding window
- Count-based sliding window는 N사이즈의 원형 배열로 구현된다.
- sliding window의 사이즈가 10이라면 원형 배열은 항상 10개의 측정값을 가지고 있다.
- 새로운 요청을 수행할때마다 집합을 수정한다.
- 이미 10개의 측정값을 가진 상태에서 새로운 값이 들어오면 가장 오래된 측정값이 퇴출된다.
1.3 Time-based sliding window
- Time-based sliding window는 N개의 부분 원형 배열로 이뤄진다.
- sliding window의 사이즈가 10이라면 10개의 부분 원형 배열을 가지게 된다.
- 부분 원형 배열을 bucket이라고 한다.
- 각 bucket은 특정 epoch 초 내에 발생한 모든 요청들을 집계한다.
- 헤드 bucket은 현재 epoch초를 저장하고 있다. (= 가장 최신정보를 저장한다.)
- 그외 bucket은 헤드 bucket 이전의 시간에서 발생한 호출 정보를 저장한다.
- bucket이 10개일때 새로운 bucket이 들어온다면 가장 오래된 bucket이 퇴출된다.
- bucket은
실패 요청 수 , 느린(slow) 요청 수 , 전체 요청 수
를 기록하고 있다. - 또한
모든 요청에 대한 전체 시간
을 기록하고 있다.
1.4 Failure rate and slow call rate thresholds
1.4.1 Failure rate
- 실패 비율이 설정한 threshold와 같거나 크면 서킷 브레이커의 상태는 CLOSED에서 OPEN 상태가 된다. (예. 집계된 요청중 50%가 실패하는 경우)
- 모든 Exception은 실패로 간주된다.
- 개발자는 어떤 Exception을 실패로 간주할지 설정할 수 있다.
1.4.2 Slow call rate
- 느린 호출 비율이 설정한 threshold보다 크거나, 같으면 서킷 브레이커의 상태는 CLOSED에서 OPEN으로 변경된다. ( 예. 집계된 요청들 중에 50%가 요청을 완료하는데 5초 이상 걸리는 경우 )
- 응답속도가 늦은 외부 시스템에 대한 로딩을 줄일 수 있다. (자체적인 대기 시간을 설정하기 때문.)
1.4.3 최소 측정 수
- 최소 측정 수를 지정하면 해당 숫자만큼 호출을 받기전까진 서킷 브레이커를 검사하지 않는다.
- 최소 측정 수를 10으로 했다면 9개의 실패 요청이 들어오더라도 그때까지 서킷 브레이커는 아무 작업도 하지 않는다.
- 서킷 브레이커는 현재 상태가 OPEN 이면
CallNotPermittedException
을 사용해서 추가적인 call을 거부한다 - OPEN 상태에서 특정 대기 시간이 경과하면 서킷 브레이커 상태가 OPEN에서 HALF_OPEN으로 변경된다. 이후에 상태 검사를 위한 몇번의 호출이 허용된다.
- 허용된 요청을 통해 상태 검사를 완료하기 전까진 추가적인 요청은
CallNotPermittedException
예외를 발생시키며 차단된다. - 실패 비율, 느린 요청 비율이 threshold보다 같거나 크면 상태는 다시 OPEN으로 변경된다.
- 그 반대의 경우 CLOSED 상태로 변경된다.
- 서킷 브레이커는 2개의 특수한 상태를 더 제공한다.
- DISABLED는 모든 접근을 허용한다.
- FORCED_OPEN은 모든 접근을 차단한다.
- 이 2개의 상태를 벗어나는 방법은 상태 전환 트리거를 사용하거나, 서킷 브레이커를 리셋하는 방법뿐이다. ( 앞서 봤던 상태와 달리 특정 로직에 의해 살아나는 상태가 아님! )
1.5 configure a CircuitBreaker
1.5.1 slidingWindowType
- 기본값은 COUNT_BASED이다.
- COUNT_BASED,TIME_BASED를 사용하면 경우
slidingWindowSize
만큼의 요청을 집계하며, threshold를 만족하는지 검사한다.
1.5.2 slidingWindowSize
- 기본값은 100이다.
- 서킷 브레이커가 CLOSED 상태일때, 집계할 요청의 개수를 뜻한다.
1.5.3 slowCallDurationThreshold
- 기본값은 60000 [ms]이다. (60초)
- 느린 요청을 나누는 기준이 되는 값이다.
1.5.4 minimumNumberOfCalls
- 기본값은 100이다.
- 서킷 브레이커 상태 검사를 위한 최소한의 요청 개수를 뜻한다.
1.5.5 failureRateThreshold
- 기본값은 50이다.
- 실패 비율을 나타낸다. 만약 집계한 요청 100개 중 50개 이상이 실패라면 (50%이상) 서킷브레이커를 OPEN상태로 바꾼다.
1.5.6 automaticTransitionFromOpenToHalfOpenEnabled
- 기본값은 false이다.
- true로 세팅하면, 서킷브레이커가 자동으로 OPEN 상태에서 HALF_OPEN 상태로 변경되며 이 과정에서 트리거를 위한 어떤 요청도 필요하지 않는다.
1.5.7 eventConsumerBufferSize
- 서킷브레이커나 벌크헤드 등 Resilience4j에서 제공하는 기능들은 자신이 발생시킨 이벤트를 기록하기 위한 버퍼를 가질 수 있으며, 그 버퍼 사이즈를
eventConsumerBufferSize
로 설정가능하다. - 서킷 브레이커에서 발생할 수 있는 이벤트로는 성공, 실패, 리셋, 상태변경, 실패무시가 있다.
2. 시나리오 정의
- 고객은 은행에서 계좌 생성을 하려고한다.
- 고객은 화면을 통해 계좌생성 API를 요청한다.
- 계좌생성 API는 계좌를 생성하기 전에 고객이 실제로 존재하는지 판단하기 위해 고객정보조회 API를 수행한다.
- 계좌생성 API는 Account Service이며, 고객정보조회 API는 Customer Service이다.
- Account Service에서 Customer Service로 API 호출을 수행해야하며, Customer Service에서 장애 발생시 Account Service에서 적절한 fallback 처리를 해줘야한다.
3. Spring에 resilience4j적용하기
3.1 Customer Service 개발
- Customer Service에서 고객정보 조회를 위한 API를 작성한다.
- 서킷 브레이커 테스트를 위해서 2초 동안 Sleep하도록 했다.
3.2 Account Service 개발
3.2.1 의존성 추가
- Spring Boot 2에 대응하는 resilience4j 의존성을 추가한다.
- 아직은 Spring 프로젝트에서 관리하지 않기 때문에 이름이 github으로 되어 있는것을 볼 수 있다.
3.2.2 서킷 브레이커 적용
- @CircuitBreaker 어노테이션을 통해 메소드에 서킷 브레이커를 적용할 수 있다.
- name에 backendA가 있는데 서킷 브레이커 인스턴스의 이름이다. 인스턴스에 대한 정보는 뒤에 application.yml에서 작성한다.
- fallbackMethod는 서킷 브레이커의 상태가 OPEN일때 요청을 수행하면 작동하는 메소드를 정의한다.
- 테스트를 위해서 고객정보 조회 API를 20회 수행하도록 한다.
3.2.3 Count Based 서킷 브레이커 테스트
- application.yml에 서킷 브레이커 인스턴스에 대한 설정을 할 수 있다.
- 이름이 backendA인 인스턴스를 생성한다.
slidingWindowType
이 COUNT_BASED이므로 최근 요청에 대한 개수를 집계한다.- 느린 요청 비율이 70% 이상이거나 실패한 요청의 비율이 70% 이상이면 상태가 OPEN으로 변경되도록 한다.
minimumNumberOfCalls
를 통해 상태 변경 검사를 위해서 최소 10개의 요청이 집계 되도록한다.slowCallDurationThreshold
를 통해서 느린 요청의 기준치를 작성해준다. 요청을 완료하는데 1초 이상 소요되면 느린 요청으로 간주된다.- 앞서 설명 했듯이 요청에 대해 Exception이 발생하면 실패로 간주한다.
- 수행 결과를 확인한다.
- 10번째 요청을 완료한 순간 실패 계산을 수행하는데, 모든 요청이 느린 요청이기 때문에 100%의 실패율을 가진다. 따라서 10번째 요청을 완료한 순간에 서킷 브레이커는 OPEN 상태가 되어 11번째 부터는 요청이 불가능하다.
- 서킷 브레이커가 OPEN 상태일때 요청을 수행하면
CallNotPermittedException
예외를 발생시킨다.
3.2.4 느린 요청 관련 설정 제거하기
- 느린 요청 관련 설정을 제거하면 실패 비율로만 서킷 브레이커의 상태를 제어하게 된다.
- Exception인 경우만 실패로 간주하는데 현재 코드는 속도는 느리지만 Exception을 발생시키진 않는다. 이때 서킷 브레이커가 어떻게 작동하는지 살펴보자.
- 예상대로 서킷 브레이커가 CLOSED 상태이므로 모든 요청에 대해서 정상적으로 수행된것을 확인할 수 있다.
- 가능하면 실패 횟수 비율과 느린 호출 비율에 대한 Threshold를 함께 사용함으로써 사용자가 장시간 대기하는 일은 없도록 하자 ( 예. 1분이 지나도록 요청에 대한 결과가 없다면 그냥 장애로 판단. )
3.2.5 minimumNumberOfCalls 값을 slidingWindowSize보다 크게 설정하기
- 아래와 같이
minimumNumberOfCalls
를 크게 변경했다. minimumNumberOfCalls
가 100이기 때문에 100번의 요청이 들어오기 전까지는 검사를 수행하지 않을것으로 예상된다.- 하지만 요청 횟수가 Sliding window size에 도달하는 순간 서킷 브레이커 상태 검사를 수행한다.
- 여기서 알 수 있듯이 minimumNumberOfCalls의 값에 도달하기 전에 slidingWindowSize의 값을 만족한다면
minimumNumberOfCalls
에 상관없이 먼저 상태검사를 수행한다. - 가능하면
minimumNumberOfCalls
는slidingWindowSize
보다 같거나 작게 작성해서 의미를 가질 수 있도록 한다. (default값은 둘다 100으로 같다.)
3.2.6 Time Based 서킷 브레이커 적용하기
slidingWindowType
을 TIME_BASED로 변경한다.slidingWindowType
은 시간을 의미하게 된다.- 10초 내에 발생한 요청 중 실패 요청 비율이 70%를 넘거나, 느린 요청의 비율이 70%를 넘어가면 서킷은 OPEN 상태가 된다.
- 10초 내에 5번의 요청이 들어왔기 때문에 검사를 시작한다.
- 사용자 조회 API를 수행하는데 최소 2초가 걸리기 때문에
minimumNumberOfCalls
를 6으로 수정하면 서킷 브레이커의 상태 검사가 영원히 발생하지 않는다. - 상태 검사를 위해선 10초 내에 발생한 요청이 최소 6번은 되어야 하는데 현재 로직상 10초내에는 아무리 많이 요청이 발생해도 5번이 최대치다.
minimumNumberOfCalls
를 6으로 수정했을 뿐인데 서킷 브레이커가 CLOSED로 유지된다.- TIME_BASED 방식을 사용하기 위해선 꽤 정교한 설정값 세팅이 필요하다. 이런 이유 때문인지
slidingWindowType
의 기본값은 COUNT_BASED이다.
4. 참고자료
'개발 > Spring' 카테고리의 다른 글
[Spring Security] Servlet Authentication Architecture (0) | 2022.08.16 |
---|---|
[Spring Security] Architecture (0) | 2022.08.15 |
[Spring MVC] 구조와 환경셋팅 ( Spring Boot의 편리함. ) (0) | 2021.06.09 |