기존 서비스를 MSA로 전환하면서, Http 요청에 대한 라이브러리로 Feign 을 채택하게 되었고, Spring Webflux 를 이용하면서 기존 Feign 이 아닌 Reactive Feign 을 사용하게 되었다. 이러한 상황에서 내부 서비스에 요청을 보낼 때 이외에도 외부 서비스에 요청을 보낼 때도 Feign 을 사용하였는데, 외부 서비스 서버의 상태에 따라 그 요청이 실패하거나, connection 이슈가 있을 수 있었다. 이 때, Retry 전략이 필요했다.
API 호출 실패에 대한 전략은 단순하지 않다. 호출 실패를 다루지 않거나, 다른 대안을 두어 fallback 처리하는 것이 해결책이 되지 않을 수 있다. 그렇지만, 성공할 때 까지 재시도하는 것도 네트워크에 부담을 가중시킬 수 있다. 따라서 전략의 하나로서, 재시도를 하는 간격(Backoff)을 설정하고, 그 간격을 점점 늘려가며 재시도하는 방법도 사용된다.
Retry 전략에 대해서(Exponential Backoff, Jitter)
Retry 설정을 위해 Feign 에서의 retry 하는 방법을 찾아보았다. Feign 에서는 Retryer 라는 것이 있어서, RetryableException 에 대해 Retryer 에서 재시도를 처리하는 메커니즘이 존재한다.
Spring Cloud Open Feign Document
하지만 위와 같이 아무 설정을 하지 않는다면, Retry 를 하지 않는 설정으로 Retryer 빈이 자동 등록된다.
따라서 Default Retryer 를 설정해주려면 다음과 같이 FeignConfig 에 Bean 으로 등록해주면 된다.
@Configuration
class FeignConfig {
@Bean
fun retryer(): Retryer {
return Retryer.Default()
}
}
이 Retryer 를 구현해서 재시도 회수, Backoff 설정 등을 지정한 CustomRetryer 를 만들 수 있는데, 이에 대한 내용은 다음에 있다.
Retrying Feign Calls
How to Customize Feign’s Retry Mechanism
문제는 Reactive Feign 에서 이를 적용했을 때, Retryer 가 동작하지 않는다는 것이었다. 이에 Reactive Feign 공식 깃허브에서 다음과 같은 내용을 찾을 수 있었다.
Reactive Feign 에서 등록될 수 있는 Bean Class 에 Retryer 가 포함되어 있지 않았다. Retry 설정과 관련된 Bean Class 는 ReactiveRetryPolicies 였는데, 이는 다음과 같이 사용될 수 있다.
@Configuration
class FeignConfig {
@Bean
fun reactiveRetryPolicy(): ReactiveRetryPolicy {
return BasicReactiveRetryPolicy.retryWithBackoff(3, 100)
}
}
이는 3번의 재시도를 시행하며, backoff 로 100ms 를 설정한다는 policy이다. 해당 class를 들여다보면,
와 같이, backoff 없이 재시도 횟수만 설정하거나, scheduler 를 설정하는 policy도 있는 것으로 보인다. 각자 상황에 맞게 설정하면 된다.
위의 방법을 이용해서 Retry를 적용을 해보았는데, 원하는 결과는 500 에러에 대해서만 Retry를 하는 거였지만, 404 에러 등 모든 에러에 대해서 Retry 를 해주고 있었다. 404 Error에 대해서 Retry 를 하는 것을 원치 않았기에, Reactive Feign 코드를 직접 열어보니 FilteredReactiveRetryPolicy 가 존재했다.
이와 같은 내용을 바탕으로 설정해준 코드는 다음과 같다
@Bean
fun reactiveRetryPolicy(): ReactiveRetryPolicy {
return FilteredReactiveRetryPolicy.notRetryOn(
BasicReactiveRetryPolicy.retryWithBackoff(3, 100),
FeignNotFoundException::class.java
)
}
ErrorDecoder 에서 status 가 404 인 경우에 대해 FeignNotFoundException 을 Throw 하게 해주어 Retry 에서 제외할 수 있게 했다
논외로, Reactor 에서도 Retry 를 지원하는데, Mono, Flux 등 Reactor 스트림에 대해서 onError 화 같은 함수가 있음을 알 수 있다. onErrorResume, onErrorMap 등의 함수를 이용해서 에러를 처리할 수 있고, retry 함수를 사용하여 해당 스트림에 대해 처음으로 돌아가서 재시도할 수 있다.
Guide to Retry in Spring WebFlux
이러한 방법을 사용하지 않은 이유는, feign client 에 대해서 전역적으로 설정을 적용하기 위해 위와 같은 방법을 사용했던 것이었다.