SlidingWindow는 CountBase와 TimeBase로 구분할 수 있다
서킷 브레이커 적용을 위한 기본 설정
resilience4j:
circuitbreaker:
configs:
default:
slidingWindowType: COUNT_BASED
minimumNumberOfCalls: 1 # 최소 7번까지는 무조건 CLOSE로 가정
slidingWindowSize: 4 # 10개 요청 단위
waitDurationInOpenState: 10s # open -> halfopen 가는데 필요한 시간
failureRateThreshold: 40 # slidingWindowSize 중 몇 % 장애 시 open으로 만들 것인지?
slowCallDurationThreshold: 3000 # 몇 ms 동안 요청이 처리되지 않으면 실패로 간주할 것인지?
slowCallRateThreshold: 60 # slidingwindow중 몇 %가 slowcall이면 open으로 간주할 것인지?
permittedNumberOfCallsInHalfOpenState: 5 # half-open 상태에서 5번까지는 close로 가기위해 호출한다
automaticTransitionFromOpenToHalfOpenEnabled: true # open 상태에서 자동으로 half-open으로 갈 것인가?
eventConsumerBufferSize: 10 # actuator를 위한 이벤트 버퍼 사이즈
recordExceptions:
- com.example.exception.ExternalApiException
ignoreExceptions: # fallback은 실행된다
- java.lang.IllegalStateException
instances:
simpleCircuitBreakerConfig:
baseConfig: default
@Service
class ExternalCBApieService(
private val externalClient: ExternalClient
) : Log {
companion object {
const val DEFAULT_CB_CONFIG = "simpleCircuitBreakerConfig"
}
@CircuitBreaker(name = DEFAULT_CB_CONFIG, fallbackMethod = "fallBack")
fun requestApi(param: String): String {
return when (param) {
"OPEN" -> externalClient.requestEx(param)
"CLOSE" -> externalClient.requestEx(param)
"TIME_OUT" -> externalClient.timeout(param)
else -> throw IllegalArgumentException()
}
}
private fun fallBack(param: String, ex: ExternalApiException): String {
log.info("api response error, state is CLOSE -> OPEN")
return ex.message ?: ""
}
private fun fallBack(param: String, ex: CallNotPermittedException): String {
log.info("CB state is open, api is not permitted")
return ex.message ?: ""
}
}
@Configuration
class ResilienceConfig : Log {
@Bean
fun circuitBreakerEventConsumer(): RegistryEventConsumer<CircuitBreaker> {
return object : RegistryEventConsumer<CircuitBreaker> {
override fun onEntryAddedEvent(entryAddedEvent: EntryAddedEvent<CircuitBreaker>) {
val eventPublisher = entryAddedEvent.addedEntry.eventPublisher
eventPublisher.onEvent { event -> log.info("{}", event) }
eventPublisher.onCallNotPermitted { event -> log.info("{}", event) } // open 상태에서 요청이 들어온 경우
eventPublisher.onStateTransition { event -> log.info("{}", event) } // state가 변경될 경우 // 다른 서버로 장애 전파할 때 사용할 수 있음
eventPublisher.onFailureRateExceeded { event -> log.info("{}", event.eventType) }
}
override fun onEntryRemovedEvent(entryRemoveEvent: EntryRemovedEvent<CircuitBreaker>) {
TODO("Not yet implemented")
}
override fun onEntryReplacedEvent(entryReplacedEvent: EntryReplacedEvent<CircuitBreaker>) {
TODO("Not yet implemented")
}
}
}
}
2024-08-27T00:05:05.875+09:00 INFO 1908 --- [demo] [nio-8080-exec-1] ResilienceConfig$$SpringCGLIB$$0 : 2024-08-27T00:05:05.875944400+09:00[Asia/Seoul]: CircuitBreaker 'simpleCircuitBreakerConfig' changed state from CLOSED to OPEN