Retry is an important tool in our arsenal to make robust and efficient APIs, it helps us to automatically reinvoke a failed operation. This is because sometimes due to network glitches or DB errors, the call fails for the first time however succeeds next time. We can do retry using annotations, and RetryTemplate.
Spring Retry using Annotation
Step -1: Add Maven dependency
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>${version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${version}</version>
</dependency>
Step -2 : Enable Retry
@EnableRetry
@SpringBootApplication
public class Application {
public static void main(String[] args) {
Application.run(Application.class, args);
}
}
Step -3 : Create Retryable Method
- @Retryable – would retry for 2 more times after first one, only if we receive a Exception of type CustomException, and Backoff of 200
- @Recover – if we don’t get any success response after 3 retry, then the code falls to here and returns a handled response.
@Service
public class ExternalService {
@Retryable(
value = {CustomException.class},
maxAttempts = 3, backoff = @Backoff(200))
public String invokeDownStreamService() throws CustomException {
....
}
@Recover
public String recover(Throwable t) {
....
}
}
Note : Spring AOP works on object AOP proxies. https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-understanding-aop-proxies. Hence for retry to work you need to invoke Retryable on methods or interfaces
Spring Retry using RetryTemplate
Step – 1: Add Maven dependency
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.5.RELEASE</version>
</dependency>
Step -2 : Create a customRetryTemplate
Create a CustomRetryTemplate class, Below we have a customRetryTemplate which would
- FixedBackOffPolicy : will retry after a backoff period of 100ms.
- SimpleRetryPolicy : would retry for 2 more times after first one, only if we receive a Exception of type MyException.
@Configuration
public class CustomRetryTemplate {
public static final int MAX_ATTEMPTS = 3;
public static final long BACKOFF_PERIOD = 100;
@Bean
public RetryTemplate customRetryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(BACKOFF_PERIOD);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
Map<Class<? extends Throwable>, Boolean> retryableException=
Collections.singletonMap(MyException.class,
Boolean.TRUE);
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(MAX_ATTEMPTS, retryableException);
retryTemplate.setRetryPolicy(retryPolicy);
return retryTemplate;
}
}
Step -3 : Execute CustomRetryTemplate
RetryTemplate provides different methods to invoke code in retry-able way, an example is the retryTemplate.execute(RetryCallback rc).
customRetryTemplate.execute(retryCallback -> {
return myService.invokemethod(...);
});