spring-retry icon indicating copy to clipboard operation
spring-retry copied to clipboard

Spring Retry + Hystrix issue

Open solidgains opened this issue 8 years ago • 4 comments

I have a @HystrixCommand annotated method that makes a restTemplate call. I also have another one that makes a SOAP call. When an exception is thrown inside the hystrixcommand annotated method, it returns a HystrixRuntimeException, and nested in that exception is the actual exception that was thrown, for instance httpClientErrorException, socketTimeout, or SoapFault.

I call these hystrixCommands from within a Spring RetryTemplate, but the problem is that I am having trouble make the RetryTemplate only retry on certain exceptions that are the nested exceptions within the HystrixRuntimeException.

The scenario now is such that, Spring retryTemplate will only retry if there is a HystrixRuntime Exception, I want to retry based on the excpetions nested within the hystrixRuntime Exception.

AccountClient.java

@Service
public class AccountClient {

    @Inject
    private Properties properties;

    @Inject
    private RestTemplate restTemplate;

    @HystrixCommand()
    public Account getAccount(String accountId){
        String url = properties.getAccountBaseUrl() + properties.getGetAccountEndpoint() + "accountId={accountId}";
        ResponseEntity<Account> response = restTemplate.getForEntity(url, Account.class, accountId);
        return response.getBody();
    }

}

AccountService.java

@Service
public class AccountService {

    @Inject
    private AccountClient accountClient;

    @Inject
    private RetryTemplate retryTemplate;

    private static final Logger log = LoggerFactory.getLogger(AccountService.class);

    public Account getAccount(String accountId){
        Account account = retryTemplate.execute(new RetryCallback<Account, RuntimeException>() {
            public Account doWithRetry(RetryContext context) {
                log.info("Retry Attempt: " + context.getRetryCount() + " Exception thrown: " + context.getLastThrowable());
                Account account = accountClient.getAccount(accountId);
                return account;
            }
        });
        return account;
    }
}

RetryTemplate bean in @Configuration class

@Bean
    public RetryTemplate retryTemplate() {
        Map<Class<? extends Throwable>, Boolean> includeExceptions = new HashMap<>();
        String[] exceptions = properties.getExceptions().split(" ");
        List<String> exceptionz = Arrays.asList(exceptions);
        for (String e : exceptionz) {
            Class<Exception> exc = null;
            try {
                exc = (Class<Exception>) Class.forName(e);
            } catch (ClassNotFoundException ex) {
                // ex.printStackTrace(); TODO log here
            }
            includeExceptions.put(exc, true);
        }
        SimpleRetryPolicy policy = new SimpleRetryPolicy(properties.getMaxRetryAttempts(), includeExceptions);
        RetryTemplate template = new RetryTemplate();
        template.setRetryPolicy(policy);
        return template;
    }

solidgains avatar Jul 06 '16 20:07 solidgains

Nesting @HystrixCommand inside a retry callback is somewhat of an odd thing to do (Hystrix has retry built in). You could fix it with a custom exception classifier if you need to, but I reckon it's better to choose one or the other (spring-retry, or hystrix). Hystrix has more features, so they aren't interchangeable (but note that @CircuitBreaker is coming in spring-retry 1.2.0.

dsyer avatar Aug 23 '16 09:08 dsyer

[...] Hystrix has retry built in [...]

@dsyer could you plz specify? I never came around a first-class citizen in Hystrix that would allow me to configure retries properly... also Netflix states:

Hystrix treats the execution that gets wrapped as a black-box. Because only some executions benefit from a retry policy, Hystrix does not add the complexity of retry.

jbspeakr avatar Jun 27 '17 08:06 jbspeakr

I guess I was confused. We use Hystrix with Ribbon/Feign/Zuul so much I probably thought the retry features were in the former not the latter. If you work out how to customize the exception classifier so it works please consider sending a pull request (here or to spring-cloud-netflix, if it is Hystrix specific). Or use @CircuitBreaker instead.

dsyer avatar Jun 27 '17 08:06 dsyer

I noticed that SimpleRetryPolicy has this constructor public SimpleRetryPolicy(int maxAttempts, Map<Class<? extends Throwable>, Boolean> retryableExceptions, boolean traverseCauses). If the exception is nested within the HystrixRuntimeException then if you set traverseCauses then it might work.

tabiul avatar Oct 19 '17 03:10 tabiul