🤞 personal home page: @Qingcheng sequence member Stone 🤞 fan benefits: plus one to one fan group to answer questions, get free rich resume template, improve learning materials, do a good job in the new era of volume king!
Follow up on the previous post, “Still Writing Retries, try Spring Retry (1)” by clicking here.
Four, RetryTemplate
The RetryTemplate is an alternative to annotated retries.
4.1 RetryOperations
The Spring Retry providesRetryOperations policy using the RetryOperations interface.
public interface RetryOperations {
<T> T execute(RetryCallback < T > retryCallback) throws Exception;
// other execute methods
<T> T execute(RetryCallback < T > retryCallback, RecoveryCallback < T > recoveryCallback,
RetryState retryState) throws Exception;
}
Copy the code
The RetryCallback allows the insertion of business logic that needs to be retried in the event of a failure.
public interface RetryCallback <T> {
T doWithRetry(RetryContext context) throws Throwable;
}
Copy the code
4.2 RetryTemplate
The RetryTemplate provides a concrete implementation of RetryOperations. It is considered good practice to create beans from it.
@Bean
@ConditionalOnMissingBean
public RetryTemplate retryTemplate(){
final SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy();
simpleRetryPolicy.setMaxAttempts(4);
final FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(1000L);
return RetryTemplate.builder()
.customPolicy(simpleRetryPolicy)
.customBackoff(fixedBackOffPolicy)
.retryOn(CustomRetryException.class)
.build();
}
Copy the code
Unit testing.
@Autowired pivate RetryTemplate retryTemplate; @Test void retryWithoutAnnotation(){ try { String message = retryTemplate.execute(x -> retryService.retryWithoutAnnotation()); log.info("message = "+message); } catch (CustomRetryException e) { log.error("Error while executing test {}",e.getMessage()); }}Copy the code
As shown in the following figure, the policy that has been retried for four times is not recovered.
4.2 RecoveryCallback
Execute, you can run RecoveryCallback to confirm that the recovery behavior is still abnormal after the retry is complete. The method signature is as follows.
public final <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback) throws E {
return this.doExecute(retryCallback, recoveryCallback, (RetryState)null);
}
Copy the code
So, let’s customize RecoveryCallback.
@Slf4j public class CustomRecoveryCallback implements RecoveryCallback<String> { @Override public String recover(RetryContext retryContext) throws Exception { log.info("Default Retry service test,total retry {}",retryContext.getRetryCount()); return "Error Class :: " + retryContext.getLastThrowable().getClass().getName(); }}Copy the code
Then unit test.
@Autowired private CustomRecoveryCallback customRecoveryCallback; @Test void retryWithoutAnnotation(){ try { String message = retryTemplate.execute(x -> retryService.retryWithoutAnnotation(), customRecoveryCallback); log.info("message = "+message); } catch (CustomRetryException e) { log.error("Error while executing test {}",e.getMessage()); }}Copy the code
As shown in the output below, after the retry, our custom RECOVER is executed.
Fifth, RetryListenerSupport
What if we wanted to set up some event listening handling mechanism at different stages throughout the retry life cycle? Setting up a custom RetryListenerSupport can help. We inherit RetryListenerSupport and Override the close, onError, and open methods, respectively
- When all retries end
- Each retry occurs when an exception occurs
- Retry before the official start.
@Slf4j public class DefaultListenerSupport extends RetryListenerSupport { @Override public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { log.info("DefaultListenerSupport close"); super.close(context, callback, throwable); } @Override public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { log.info("DefaultListenerSupport onError"); super.onError(context, callback, throwable); } @Override public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) { log.info("DefaultListenerSupport open"); return super.open(context, callback); }}Copy the code
And when the RetryTemaplate is constructed, set the withListener field.
@Bean @ConditionalOnMissingBean public RetryListenerSupport retryListenerSupport(){ return new DefaultListenerSupport(); } @Bean @ConditionalOnMissingBean public RetryTemplate retryTemplate(RetryListenerSupport retryListenerSupport){ ... return RetryTemplate.builder() .customPolicy(simpleRetryPolicy) .customBackoff(fixedBackOffPolicy) .withListener(retryListenerSupport) .retryOn(CustomRetryException.class) .build(); }Copy the code
Run the unit test and the output is as follows.
Six, summarized
In this article, we’ve looked at the different features of Spring Retry, and it’s clear that using it can make applications more robust. We have implemented some of the most common uses of Spring Retry, including the @retryable annotation and RetryTemplate.
So where can we use Spring Retry? There are two suggestions
- Use retry only for temporary errors. It is not recommended to use it in permanent errors, as this can cause system performance problems.
- It is not an alternative to fuses. It is best to use both fuses and retries when allowed.
Boy, haven’t you seen enough? Click on the details of the stone, casually have a look, maybe there is a surprise? Welcome to support the likes/attention/comments, your support is my biggest motivation, thank you!