Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spring Retry + Hystrix issue #46

Open
solidgains opened this issue Jul 6, 2016 · 4 comments
Open

Spring Retry + Hystrix issue #46

solidgains opened this issue Jul 6, 2016 · 4 comments

Comments

@solidgains
Copy link

solidgains commented Jul 6, 2016

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;
    }
@dsyer
Copy link
Member

dsyer commented Aug 23, 2016

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.

@jbspeakr
Copy link

[...] 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.

@dsyer
Copy link
Member

dsyer commented Jun 27, 2017

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.

@tabiul
Copy link

tabiul commented Oct 19, 2017

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants