I have a problem with configuring fallbacks for feign clients. Lets say I have client like this:
@FeignClient(name = "some-client", url = "${some-client.url}")
public interface SomeClient {
@GetMapping(value = "/api/some-endpoint", produces = MediaType.APPLICATION_JSON_VALUE)
List<SomeDto> searchForAllDtos(@RequestParam("param") String param);
}
I have configured simple retryer like this:
@Configuration
public class ClientConfiguration {
@Bean
public Retryer retryer(@Value("${http.retry.period}") long period,
@Value("${http.retry.maxPeriod}") long maxPeriod,
@Value("${http.retry.maxAttempts}") int maxAttempts) {
return new CustomRetryer(period, maxPeriod, maxAttempts);
}
@Bean
public ErrorDecoder decoder() {
return new CustomDecoder();
}
}
@Slf4j
class CustomRetryer extends Retryer.Default {
private final int maxAttempts;
private final long period;
private final long maxPeriod;
public CustomRetryer(long period, long maxPeriod, int maxAttempts) {
super(period, maxPeriod, maxAttempts);
this.period = period;
this.maxPeriod = maxPeriod;
this.maxAttempts = maxAttempts;
}
private CustomRetryer(CustomRetryer retryer) {
super(retryer.period, retryer.maxPeriod, retryer.maxAttempts);
this.period = retryer.period;
this.maxPeriod = retryer.maxPeriod;
this.maxAttempts = retryer.maxAttempts;
}
@Override
public void continueOrPropagate(RetryableException ex) {
super.continueOrPropagate(ex);
log.warn("Retrying HTTP request...");
}
@Override
public Retryer clone() {
return new CustomRetryer(this);
}
}
class CustomDecoder implements ErrorDecoder {
public Exception decode(String methodKey, Response response) {
if (response.status() > 499) {
throw new RetryableException(
response.status(),
String.format("Service unavailable (status code %s)", response.status()),
response.request().httpMethod(),
null,
response.request());
} else {
return new RuntimeException(
String.format("Error with status code %s received after HTTP request", response.status()));
}
}
}
Everything worked well until I wanted to add fallback when call is still failing after configured max attempts. I have changed my client like this:
@FeignClient(name = "some-client", url = "${some-client.url}", fallback = SomeClientFallback.class)
public interface SomeClient {
// etc.
}
@Component
class SomeClientFallback implements SomeClient {
@Override
public List<SomeDto> searchForAllDtos(String param) {
return Collections.emptyList();
}
}
It was not working, exception was still thrown instead of routing to callback, I have read that hystrix must be enabled so I have added feign.hystrix.enabled=true to my application.properties file. Everything seems to worked well by after investigation and writing some tests I have found out that retry is only used once when I have configured it for 5 max attempts. Looks like there is some conflict with hystrix timeout which is set to 1000ms by default. I have tried to increase it via hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds prop but looks like it has no effect. Did I miss something?
There is spring-retry project which I could use but it requires to add @Retryable annotation to evey method and I would like to have default retry for all feign clients. I would appreciate all help and ideas.