(Reactive) Feign Retry 전략

기존 서비스를 MSA로 전환하면서, Http 요청에 대한 라이브러리로 Feign 을 채택하게 되었고, Spring Webflux 를 이용하면서 기존 Feign 이 아닌 Reactive Feign 을 사용하게 되었다. 이러한 상황에서 내부 서비스에 요청을 보낼 때 이외에도 외부 서비스에 요청을 보낼 때도 Feign 을 사용하였는데, 외부 서비스 서버의 상태에 따라 그 요청이 실패하거나, connection 이슈가 있을 수 있었다. 이 때, Retry 전략이 필요했다.

전략적인 Retry

API 호출 실패에 대한 전략은 단순하지 않다. 호출 실패를 다루지 않거나, 다른 대안을 두어 fallback 처리하는 것이 해결책이 되지 않을 수 있다. 그렇지만, 성공할 때 까지 재시도하는 것도 네트워크에 부담을 가중시킬 수 있다. 따라서 전략의 하나로서, 재시도를 하는 간격(Backoff)을 설정하고, 그 간격을 점점 늘려가며 재시도하는 방법도 사용된다.

Retry 전략에 대해서(Exponential Backoff, Jitter)

기존 Feign 에서의 Retry

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 에서의 Retry 설정

문제는 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도 있는 것으로 보인다. 각자 상황에 맞게 설정하면 된다.

FilteredReactiveRetryPolicy

위의 방법을 이용해서 Retry를 적용을 해보았는데, 원하는 결과는 500 에러에 대해서만 Retry를 하는 거였지만, 404 에러 등 모든 에러에 대해서 Retry 를 해주고 있었다. 404 Error에 대해서 Retry 를 하는 것을 원치 않았기에, Reactive Feign 코드를 직접 열어보니 FilteredReactiveRetryPolicy 가 존재했다.

  • Retry 를 할 Exception 또는 Retry 를 하지 않을 Exception 을 지정해줄 수 있다
  • 기본 생성자로 FilteredReactiveRetryPolicy를 생성하는 경우에는 Retry 해줄 Exception 을 지정해준다
  • 첫번째 인자로는 ReactiveRetryPolicy 구현체를 받으며, 두번째 인자로는 Retry 해줄 Exception 을 받는다
  • 반대로 Retry를 하지 않을 Exception을 지정하는 경우는 FilteredReactiveRetryPolicy.notRetryOn() 함수를 통해 생성한다
  • 마찬가지로 첫번째 인자로는 ReactiveRetryPolicy 구현체, 두번째 인자로는 Retry 에서 제외할 Exception 을 받는다

이와 같은 내용을 바탕으로 설정해준 코드는 다음과 같다

	@Bean
    fun reactiveRetryPolicy(): ReactiveRetryPolicy {
        return FilteredReactiveRetryPolicy.notRetryOn(
            BasicReactiveRetryPolicy.retryWithBackoff(3, 100),
            FeignNotFoundException::class.java
        )
    }

ErrorDecoder 에서 status 가 404 인 경우에 대해 FeignNotFoundException 을 Throw 하게 해주어 Retry 에서 제외할 수 있게 했다

Reactor 에서의 Retry

논외로, Reactor 에서도 Retry 를 지원하는데, Mono, Flux 등 Reactor 스트림에 대해서 onError 화 같은 함수가 있음을 알 수 있다. onErrorResume, onErrorMap 등의 함수를 이용해서 에러를 처리할 수 있고, retry 함수를 사용하여 해당 스트림에 대해 처음으로 돌아가서 재시도할 수 있다.

Guide to Retry in Spring WebFlux

이러한 방법을 사용하지 않은 이유는, feign client 에 대해서 전역적으로 설정을 적용하기 위해 위와 같은 방법을 사용했던 것이었다.

좋은 웹페이지 즐겨찾기