Resilience4j
- Java에서 회복 탄력성을 관리하기 위한 라이브러리
- Circuit Breaker, Retry, Rate Limiter, Bulkhead 등 다양한 패턴 제공
Circuit Breaker
- 서비스 간의 실패를 관리하고 격리하는 데 사용
- 특정 서비스가 일정 시간 동안 많은 실패를 하면 해당 서비스로의 호출 차단
- 전체 시스템의 성능 저하 및 장애 방지
- 상태
- Closed: 정상 상태. 모든 요청이 정상적으로 서비스로 전달
- Open: 실패율이 임계 값을 초과했을 때 서비스로의 호출 차단 모든 요청 즉시 실패
- Half-Open: 일정 시간이 지나면 일부 요청 허용해서 서비스가 정상으로 돌아왔는지 확인 → 성공하면 Closed, 실패하면 Open 상태로 전환
- 설정
- slidingWindowSize: Circuit Breaker가 상태를 결정할 때 사용하는 슬라이딩 윈도우의 크기
- failureRateThreshold: 실패율 임계 값. 이 임계 값을 초과하면 Open 상태 전환
- waitDurationInOpenState: Circuit Breaker가 Open 상태에서 Half-Open 상태로 전환되기까지 대기하는 시간
Retry
- 일시적인 오류가 발생했을 때 자동으로 재시도를 수행
- 재시도 횟수 및 각 시도 간의 대기 시간 설정 가능
- maxAttempts: 최대 재시도 횟수
- waitDuration: 각 재시도 사이의 대기 시간
예제 컨트롤러를 1초 미만의 간격으로 계속해서 호출해 보았다.
서킷 브레이커는 설정된 실패율이 일정 수준을 넘으면 Open 상태가 된다.
그리고 retry는 실패한 요청을 일정 횟수까지 재시도하도록 설정된다.
즉, 500 에러가 나온 것은 서킷 브레이커나 재시도 로직이 작동하면서 발생한 것 같다.
- 200 OK : 처음 요청이 들어오면 정상 응답
- 500 Error : 일정 횟수의 오류가 발생하면 서킷 브레이커가 Open 상태 전환
- Retry : 재시도 로직에 따라 요청을 다시 시도하면서 성공적인 응답 받기 가능 → 200 OK
- 상태 변화 : 서킷 브레이커가 Half-Open 상태로 전환되고, 성공적인 요청이 오면 다시 Closed 상태 전환
@Configuration
public class Resilience4JConfig {
// 전역 커스텀 서킷 브레이커 설정을 위한 Bean 정의
@Bean
public Customizer<Resilience4JCircuitBreakerFactory> globalCustomConfiguration() {
// 서킷 브레이커 설정 정의
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED) // 슬라이딩 윈도우 타입을 호출 횟수 기반으로 설정
.slidingWindowSize(20) // 슬라이딩 윈도우 크기
.minimumNumberOfCalls(20) // 최소 호출 수
.waitDurationInOpenState(Duration.ofSeconds(30)) // 서킷 브레이커가 오픈 상태를 유지하는 시간
.failureRateThreshold(25) // 실패율 임계값
.slowCallDurationThreshold(Duration.ofSeconds(2)) // 느린 호출로 간주되는 시간
.slowCallRateThreshold(25) // 느린 호출이 전체 호출 중 25% 이상이면 서킷 브레이커가 열리도록 설정
.permittedNumberOfCallsInHalfOpenState(10) // 반 개방 상태에서 허용되는 호출 수
.automaticTransitionFromOpenToHalfOpenEnabled(true) // 자동으로 오픈에서 반 개방 상태로 전환하도록 설정
.build();
// 타임 리미터 설정 정의
TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()
.timeoutDuration(Duration.ofSeconds(3)) // 타임아웃 시간
.build();
// 서킷 브레이커 및 타임 리미터 설정을 적용
return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
.timeLimiterConfig(timeLimiterConfig) // 타임 리미터 적용
.circuitBreakerConfig(circuitBreakerConfig) // 서킷 브레이커 설정 적용
.build()
);
}
}
public class OrderApiController {
// 주문 생성
@PostMapping
@CircuitBreaker(name = "orderServiceCircuitBreaker", fallbackMethod = "fallbackMethod")
public ResponseEntity<?> createOrder(HttpServletRequest request, @RequestBody OrderRequestDto orderRequestDto) {}
}
부하가 많이 일어나는 메서드들에 서킷 브레이커 어노테이션을 달아 주었다.
여기서 Resilience4jConfig에서 설정한 전역 서킷 브레이커 및 타임 리미터 설정이 자동으로 적용된다.
컨트롤러에 붙이는 이유
1. 단일 책임 원칙
➡ 인프라스트럭처 관련 로직은 컨트롤러나 AOP 같은 외부 클래스로 분리하는 것이 바람직
2. 인프라스트럭처 관련 설정 분리
3. 명확한 에러 처리
'log.info' 카테고리의 다른 글
[Kafka] 이론 (0) | 2024.06.30 |
---|---|
[Spring Security] JWT Refresh Token (리프레시 토큰) (0) | 2024.06.20 |
[Docker] Docker Compose (0) | 2024.06.18 |
[Docker] 볼륨 vs 바인드 마운트 (1) | 2024.06.17 |
[Redis] 데이터 추가 및 관리 명령어 (0) | 2024.06.17 |