In the previous study of fault tolerance mechanism theory, we found that failback automatic recovery is one of the processing methods. Spring-retry is the middleware implementing failback. Let’s start learning about spring-retry
Basic framework Examples
The framework is introduced
- The RetryTemplate, the retry template, is the overall process entry into the Spring-Retry framework
- The RetryCallback interface encapsulates the service code. After failback, the RetryCallback interface is invoked again
- RetryPolicy, the RetryPolicy, describes how the RetryCallback interface will be called
Use the Spring Retry example
public class RetryTemplateDemo {
/** ** business code */
private static String business(a) throws Exception {
System.out.println("begin...");
if (true) {
throw new Exception();
}
System.out.println("end...");
return "success";
}
public static void main(String[] args) throws Exception {
RetryTemplate template = new RetryTemplate();
SimpleRetryPolicy policy = new SimpleRetryPolicy(3);
template.setRetryPolicy(policy);
RetryCallback<String, Exception> retryCallback = (context) -> {
returnbusiness(); }; String result = template.execute(retryCallback); System.out.println(result); }}Copy the code
The test results
22:24:53.746 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=0
begin...
22:24:53.751 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=1
22:24:53.751 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=1
begin...
22:24:53.751 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=2
22:24:53.751 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=2
begin...
22:24:53.751 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=3
22:24:53.751 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry failed last attempt: count=3
Exception in thread "main" java.lang.Exception
at priv.dengjili.spring.retry.demo.test.RetryTemplateDemo5.business(RetryTemplateDemo5.java:44)
at priv.dengjili.spring.retry.demo.test.RetryTemplateDemo5.lambda$0(RetryTemplateDemo5.java:33)
at priv.dengjili.spring.retry.demo.test.RetryTemplateDemo5$$Lambda$1/1221555852.doWithRetry(Unknown Source)
at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:329)
at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:209)
at priv.dengjili.spring.retry.demo.test.RetryTemplateDemo5.main(RetryTemplateDemo5.java:35)
Copy the code
The result shows that the original service code will throw an exception, and the SimpleRetryPolicy is set to retry three times. The test results show that the results are consistent
RetryPolicy provides other policy implementations:
- NeverRetryPolicy: RetryCallback is allowed to be called only once and retry is not allowed.
- AlwaysRetryPolicy: allows infinite retries until success. Improper logic in this way will lead to an infinite loop.
- SimpleRetryPolicy: specifies a fixed retry policy. The maximum number of retries is three by default. RetryTemplate Specifies the default policy.
- TimeoutRetryPolicy: TimeoutRetryPolicy. The default timeout is 1 second. Retries are allowed within the specified timeout.
- CircuitBreakerRetryPolicy: fusing features of retry strategy, it is necessary to set up three parameters openTimeout, resetTimeout and delegate;
- A CompositeRetryPolicy is composed of two retry policies. An optimistic retry policy is implemented as long as one policy allows retry, and a pessimistic retry policy is implemented as long as one policy does not allow retry. Regardless of the two retry policies, each policy in the CompositeRetryPolicy executes.
For detailed examples, please refer to github.com/spring-proj…
RecoveryCallback Recovery policy
- RecoveryCallback, which provides a compensation method to recover after all retries fail
public class RetryTemplateDemo {
/** ** business code */
private static String business(a) throws Exception {
System.out.println("begin...");
if (true) {
throw new Exception();
}
System.out.println("end...");
return "success";
}
public static void main(String[] args) throws Exception {
RetryTemplate template = new RetryTemplate();
SimpleRetryPolicy policy = new SimpleRetryPolicy(3);
template.setRetryPolicy(policy);
RetryCallback<String, Exception> retryCallback = (context) -> {
return business();
};
RecoveryCallback<String> recoveryCallback = (context) -> {
return "default_sucesss"; }; String result = template.execute(retryCallback, recoveryCallback); System.out.println(result); }}Copy the code
The test results
22:45:37.823 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=0
begin...
22:45:37.826 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=1
22:45:37.826 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=1
begin...
22:45:37.827 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=2
22:45:37.827 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=2
begin...
22:45:37.827 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=3
22:45:37.827 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry failed last attempt: count=3
default_sucesss
Copy the code
The result shows that the original service code throws an exception and the SimpleRetryPolicy is set to retry for three times. The result is consistent with the RecoveryCallback interface method
Stateful retry
HTTP is stateless, that is, there is no relationship or influence between two calls. Stateful means there is influence between calls. For example, if the failure is due to transaction resources, data problems may occur during multiple calls and the exception cannot be retried immediately. Spring-retry provides the RetryState object, as an example
public class RetryTemplateDemo5 {
/** ** business code */
private static String business(a) throws Exception {
System.out.println("begin...");
if (true) {
throw new Exception();
}
System.out.println("end...");
return "success";
}
public static void main(String[] args) throws Exception {
RetryTemplate template = new RetryTemplate();
SimpleRetryPolicy policy = new SimpleRetryPolicy(3);
template.setRetryPolicy(policy);
RetryCallback<String, Exception> retryCallback = (context) -> {
return business();
};
RecoveryCallback<String> recoveryCallback = (context) -> {
return "default_sucesss";
};
RetryState state = new DefaultRetryState("unique call"); String result = template.execute(retryCallback, recoveryCallback, state); System.out.println(result); }}Copy the code
The test results
22:56:59.883 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=0
begin...
22:56:59.887 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=1
22:56:59.887 [main] DEBUG org.springframework.retry.support.RetryTemplate - Rethrow in retry for policy: count=1
Exception in thread "main" java.lang.Exception
at priv.dengjili.spring.retry.demo.test.RetryTemplateDemo5.business(RetryTemplateDemo5.java:30)
at priv.dengjili.spring.retry.demo.test.RetryTemplateDemo5.lambda$0(RetryTemplateDemo5.java:41)
at priv.dengjili.spring.retry.demo.test.RetryTemplateDemo5$$Lambda$1/2104457164.doWithRetry(Unknown Source)
at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:329)
at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:255)
at priv.dengjili.spring.retry.demo.test.RetryTemplateDemo5.main(RetryTemplateDemo5.java:47)
Copy the code
The tests were as expected
RetryListener listener
Provides three places to listen
public class RetryTemplateDemo5 {
/** ** business code */
private static String business(a) throws Exception {
System.out.println("begin...");
if (true) {
throw new Exception();
}
System.out.println("end...");
return "success";
}
public static void main(String[] args) throws Exception {
RetryTemplate template = new RetryTemplate();
SimpleRetryPolicy policy = new SimpleRetryPolicy(3);
template.setRetryPolicy(policy);
RetryCallback<String, Exception> retryCallback = (context) -> {
return business();
};
RecoveryCallback<String> recoveryCallback = (context) -> {
return "default_sucesss";
};
RetryListener listener = new RetryListener() {
@Override
public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
System.out.println("open");
// It must return true, otherwise it is finished
return true;
}
@Override
public <T, E extends Throwable> void close(RetryContext context, RetryCallback
callback, Throwable throwable)
,> {
System.out.println("close");
}
@Override
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback
callback, Throwable throwable)
,> {
System.out.println("onError");
}};
// Multiple RetryListeners can be set
template.setListeners(newRetryListener[] {listener}); String result = template.execute(retryCallback, recoveryCallback); System.out.println(result); }}Copy the code
The test results
open
23: 08:58.601 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=0
begin...
onError
23: 08:58.605 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=1
23: 08:58.606 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=1
begin...
onError
23: 08:58.606 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=2
23: 08:58.606 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=2
begin...
onError
23: 08:58.606 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=3
23: 08:58.606 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry failed last attempt: count=3
close
default_sucesss
Copy the code
The tests were as expected
Other instructions
Starting with version 1.3, a simpler configuration of RetryTemplate is also available, as follows:
RetryTemplate.builder()
.maxAttempts(10)
.exponentialBackoff(100.2.10000)
.retryOn(IOException.class)
.traversingCauses()
.build();
RetryTemplate.builder()
.fixedBackoff(10)
.withinMillis(3000)
.build();
RetryTemplate.builder()
.infiniteRetry()
.retryOn(IOException.class)
.uniformRandomBackoff(1000.3000)
.build();
Copy the code
Or use declarative programming, annotated as follows: eg1:
@Configuration
@EnableRetry
public class Application {
@Bean
public Service service(a) {
return new Service();
}
@Bean public RetryListener retryListener1(a) {
return new RetryListener() {...}
}
@Bean public RetryListener retryListener2(a) {
return newRetryListener() {... }}}@Service
class Service {
@Retryable(RemoteAccessException.class)
public service(a) {
// ... do something}}Copy the code
Eg2:
@Service
class Service {
@Retryable(maxAttempts=12, backoff=@Backoff(delay=100, maxDelay=500))
public service(a) {
// ... do something}}Copy the code
Eg3:
@Service
class Service {
@Retryable(recover = "service1Recover", value = RemoteAccessException.class)
public void service1(String str1, String str2) {
// ... do something
}
@Retryable(recover = "service2Recover", value = RemoteAccessException.class)
public void service2(String str1, String str2) {
// ... do something
}
@Recover
public void service1Recover(RemoteAccessException e, String str1, String str2) {
// ... error handling making use of original args if required
}
@Recover
public void service2Recover(RemoteAccessException e, String str1, String str2) {
// ... error handling making use of original args if required}}Copy the code
Introduction to the source code
The above is my sort out by reading the Spring – Retry the source code, source code directory https://github.com/spring-projects/spring-retry
The core code entry: org. Springframework. Retry. Support. The doExecute RetryTemplate method