https://github.com/Seungkyu-Han/Toge-do-backend
GitHub - Seungkyu-Han/Toge-do-backend: Toge-do 앱의 백엔드 리포지토리입니다
Toge-do 앱의 백엔드 리포지토리입니다. Contribute to Seungkyu-Han/Toge-do-backend development by creating an account on GitHub.
github.com
MSA에서 요청을 받는 가장 상위 layer인 Gateway에 CircuitBreaker를 적용해보려 한다.
우선 CircuitBreaker에 대해 알아보자.
말 그대로 회로 차단기의 역할을 한다.
이런 서버가 있다고 생각해보자.
server B의 처리량에 제한이 있기 때문에 1초에 11번 이상 요청하면 server B에서 지연이 생기다가 에러가 반환될 것이다.
이런 상황을 막기 위해 server B로의 요청을 막고 default로 지정된 응답을 반환하는 것이다.
이렇게 CircuitBreaker는 요청하는 외부서버에서 일정량의 에러가 발생하면 해당 서버의 응답을 차단하고 default 응답을 반환하며, 조건이 충족되면 다시 해당 서버로 응답을 보내는 등, 서버에서의 장애 전파를 막는다.
Circuit Breaker는 아래와 같이 3가지의 상태가 존재한다.
- closed: 해당 서버와 정상적으로 요청을 주고 받을 수 있는 상태
- open: 해당 서버와 아예 요청을 주고 받을 수 없는 상태
- half_open: 해당 서버와 응답을 주고 받을 수는 있지만, 불안정한 상태로 실패율을 측정해 close, open으로 변경될 상태
각 상태는 위와 같은 방식으로 상호작용한다.
실패율과 성공율은 sliding window라는 큐에 저장된 성공과 실패를 확인해, 계산한다.
이제 적용해보도록 하자.
이렇게 설계되어 있는 시스템에서 Gateway에 Circuit Breaker를 설정해보려 한다.
우선 Gateway에 해당 라이브러리를 추가해준다.
implementation("org.springframework.cloud:spring-cloud-starter-circuitbreaker-reactor-resilience4j")
그러고는 yml 파일에 다음과 같이 설정해준다. (@Bean으로 설정하는 방법도 있지만, 더 간단한 yml에 설정을 해보려 한다)
resilience4j:
circuitbreaker:
instances:
user:
sliding-window-size: 10
failure-rate-threshold: 50
minimum-number-of-calls: 4
automatic-transition-from-open-to-half-open-enabled: true
permitted-number-of-calls-in-half-open-state: 4
wait-duration-in-open-state: 5s
schedule:
sliding-window-size: 10
failure-rate-threshold: 50
minimum-number-of-calls: 4
automatic-transition-from-open-to-half-open-enabled: true
permitted-number-of-calls-in-half-open-state: 4
wait-duration-in-open-state: 5s
timelimiter:
instances:
user:
timeout-duration: 3s
schedule:
timeout-duration: 3s
(sliding-window-size): sliding window의 큐 사이즈
(failure-rate-threshold): 실패율이 해당 비율을 넘으면 회로가 차단
(minimun-number-of-calls): 성공률과 실패율을 계산 할 때는 큐에 최소 n개의 요소가 존재해야 함
(automatic-transition-from-open-to-half-open-enabled): 회로가 지정된 시간 이후에 자동으로 open에서 half_open으로 전환
(permitted-number-of-calls-in-half-open-state): half_open 상태에서 받을 요청의 개수
(wait-duration-in-open-state): open 상태에서 대기할 시간
timelimiter에서는 각 서버로부터의 응답이 설정한 시간을 넘으면 에러로 판단하고 지정된 응답을 반환한다는 것이다.
이렇게 CircuitBreaker들을 설정을 했으며, 이제 각 라우터에 적용해보자.
spring:
cloud:
gateway:
routes:
- id: user-security
uri: ${USER_URI}
predicates:
- Path=/api/v1/friend/**
filters:
- AuthorizationFilter
- name: CircuitBreaker
args:
name: user
fallbackUri: forward:/fallback/user
해당 라우터에 필터로 CircuitBreaker라는 이름으로 필터를 등록해준다.
args의 name으로 CircuitBreaker의 이름을 지정하고, fallbackUri를 통해 지정된 default 응답을 설정한다.
이렇게 만들었으니 테스트를 해보자.
@GetMapping("/CircuitBreaker-test")
suspend fun test(@RequestParam flag: Int): ResponseEntity<HttpStatus>{
if (flag == 1)
Mono.delay(Duration.ofSeconds(10)).awaitSingle()
return ResponseEntity.ok().build()
}
이렇게 flag를 1로 주면 10초의 지연이 발생하기에, timelimter의 시간인 3초를 넘어 실패로 간주하고 지정된 응답을 반환하도록 만든 api가 있다.
이렇게 1로 요청을 해보니, 3초가 지나 지정된 응답으로 503과 문자열이 나오는 것을 볼 수 있었다.
그럼 이제 실패율을 50% 넘겨보고, 그 후 요청을 해보며 어떤 응답이 나오는지 보자.
빠르게 3번을 1로 요청해보며, minimum number의 4번에서 실패율을 50%를 넘겨보니
0을 요청해도 바로 실패가 반환하는 것을 볼 수 있었다.
지정한 시간인 5초 이후에 다시 요청해보자.
이번에는 같은 값으로 요청했음에도 200이 나오는 것을 볼 수 있었다.
이렇게 Gateway에 기본적인 Circuit breaker를 설정해보았다.
아직 Gateway를 제외하면 서버 간에 통신이 존재하지 않아서, 이 정도만 설정을 하겠지만 중요한 기술이니 앞으로도 계속 공부해두도록 해야겠다.
'토이 프로젝트' 카테고리의 다른 글
MSA에서 Kafka없이 MongoDB만으로 채팅서버 구현하기 (1) | 2025.01.03 |
---|---|
WebFlux SwitchIfEmpty가 항상 시작되는 에러 (2) | 2024.12.23 |
Webflux에 transaction 적용 (0) | 2024.12.13 |
FCM을 사용한 실시간 알림 서비스 (0) | 2024.12.13 |
SSE를 사용한 앱 실행 중 알림 서비스 (0) | 2024.12.13 |