First, environment construction
- Add the SpringRetry dependency, which is implemented using AOP, so add the AOP package as well
<! -- SpringRetry -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
Copy the code
- The official documentation
- www.baeldung.com/spring-retr…
Second, the RetryTemplate
2.1 RetryTemplate
- RetryTemplate encapsulates the Retry basic operation
- org.springframework.retry.support.RetryTemplate
- In the RetryTemplate file, you can specify the listening, rollback policy, and retry policy
- Just the normal New RetryTemplate() is needed
2.2 RetryListener
- RetryListener specifies a callback when an error occurs during execution
- org.springframework.retry.RetryListener
package org.springframework.retry;
public interface RetryListener {
/** * is called only once when the task starts executing
<T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback);
/** * Call at the end of the task (including retry), only once */
<T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);
/** * callback when an error occurs
<T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);
}
Copy the code
- Once configured, specify it in the RetryTemplate
2.3 Rollback Policy
2.3.1 FixedBackOffPolicy
- How much time is delayed to continue the call when an error occurs
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(1000L);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
Copy the code
- Once configured, specify it in the RetryTemplate
2.3.2 ExponentialBackOffPolicy
- The delay is exponential after the first delay at the specified delay time when an error occurs
// Exponential rollback (s) : the first rollback is 1s, the second rollback is 2s, the third rollback is 4 seconds, and the fourth rollback is 8 seconds
ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy();
exponentialBackOffPolicy.setInitialInterval(1000L);
exponentialBackOffPolicy.setMultiplier(2);
retryTemplate.setBackOffPolicy(exponentialBackOffPolicy);
Copy the code
- Once configured, specify it in the RetryTemplate
2.4 Retry Policy
- The retry policy specifies the number of retries when an error occurs
// Retry policy
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(5);
retryTemplate.setRetryPolicy(retryPolicy);
Copy the code
- Once configured, specify it in the RetryTemplate
2.5 RetryCallback
- RetryCallback is the callback that is executed when retryTemplate. Execute
- public final <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback) throws E
2.6 Core Usage
- You can use RetryTemplate for simple use
- Configuration retryTemplate
- The rollback policy is ExponentialBackOffPolicy
- Set the retry policy to SimpleRetryPolicy
- Specify the listener, RetryListener
import com.codecoord.util.PrintUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
@Configuration
public class RetryTemplateConfig {
/** * Inject retryTemplate */
@Bean
public RetryTemplate retryTemplate(a) {
RetryTemplate retryTemplate = new RetryTemplate();
/// Roll back fixed time (s)
/* FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); fixedBackOffPolicy.setBackOffPeriod(1000L); retryTemplate.setBackOffPolicy(fixedBackOffPolicy); * /
// Exponential rollback (s) : the first rollback is 1s, and the second rollback is 2s
ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy();
exponentialBackOffPolicy.setInitialInterval(1000L);
exponentialBackOffPolicy.setMultiplier(2);
retryTemplate.setBackOffPolicy(exponentialBackOffPolicy);
// Retry policy
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(5);
retryTemplate.setRetryPolicy(retryPolicy);
// Set the listener to open and close at startup and end, respectively
RetryListener[] listeners = {
new RetryListener() {
@Override
public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
PrintUtil.print("open");
return true;
}
@Override
public <T, E extends Throwable> void close(RetryContext context, RetryCallback
callback, Throwable throwable)
,> {
PrintUtil.print("close");
}
@Override
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback
callback, Throwable throwable)
,> {
PrintUtil.print("onError"); }}}; retryTemplate.setListeners(listeners);returnretryTemplate; }}Copy the code
- Inject RetryTemplate into the Controller, or optionally into the Service
@RestController
public class SpringRetryController {
@Resource
private RetryTemplate retryTemplate;
private static int count = 0;
@RequestMapping("/retry")
public Object retry(a) {
try {
count = 0;
retryTemplate.execute((RetryCallback<Void, RuntimeException>) context -> {
// Business code
/ /...
// Simulation throws an exception
++count;
throw new RuntimeException("Throw an exception");
});
} catch (RuntimeException e) {
System.out.println("Exception");
}
return "retry = "+ count; }}Copy the code
- Access the Retry interface and observe the log output
18:27:20.648 - http-nio-8888-exec-1 - open
18:27:20.649 - http-nio-8888-exec-1- retryTemplate. Execute execution18:27:20.649 - http-nio-8888-exec-1 - onError
18:27:21.658 - http-nio-8888-exec-1- retryTemplate. Execute execution18:27:21.658 - http-nio-8888-exec-1 - onError
18:27:23.670 - http-nio-8888-exec-1- retryTemplate. Execute execution18:27:23.670 - http-nio-8888-exec-1 - onError
18:27:27.679 - http-nio-8888-exec-1- retryTemplate. Execute execution18:27:27.679 - http-nio-8888-exec-1 - onError
18:27:35.681 - http-nio-8888-exec-1- retryTemplate. Execute execution18:27:35.681 - http-nio-8888-exec-1 - onError
18:27:35.681 - http-nio-8888-exec-1 - close
Copy the code
Third, EnableRetry
- EnableRetry Enables retry. Methods will be executed by default when specified on a class. Retry three times
- Define the service, enable the @enableretry annotation, and specify @retryable. Retry is referred to in the next section
import org.springframework.retry.annotation.Retryable;
public interface RetryService {
/** * Retry method call */
@Retryable
void retryServiceCall(a);
}
Copy the code
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.stereotype.Service;
@EnableRetry
@Service
public class RetryServiceImpl implements RetryService {
@Override
public void retryServiceCall(a) {
PrintUtil.print("Method call..");
throw new RuntimeException("Manual exception"); }}Copy the code
- Service is injected into the controller
@RequestMapping("/retryAnnotation")
public Object retryAnnotation(a) {
retryService.retryServiceCall();
return "retryAnnotation";
}
Copy the code
- Retry by default
18:46:48.721 - http-nio-8888-exec-1- Method calls..18:46:49.724 - http-nio-8888-exec-1- Method calls..18:46:50.730 - http-nio-8888-exec-1- Method calls.. Java. Lang. RuntimeException: manual exceptionCopy the code
Four, Retryable
- Annotations for methods that need to be retried
- There are several properties
- Retryable specifies the annotation parameter
- Value: specifies the exception that occurs to retry
- Include: Is null by default, as is value. If exclude is also null, all exceptions are retried
- Exclude: Specifies that exceptions are not retried. The default value is empty. If include is also empty, all exceptions are retried
- Maxtem ps: The number of retries. The default is 3
- Backoff: Retry compensation mechanism. No default
- @backoff annotate retry compensation policy
- If no parameter is set, FixedBackOffPolicy is used by default and the retry time is 1000ms
- Set delay, using FixedBackOffPolicy (when delay and maxDealy are set, retry wait is evenly distributed between these two values)
- Set delay, maxDealy, and multiplier using ExponentialBackOffPolicy (an implementation of an exponential retry interval). A multiplier specifies a multiple of the delay, for example delay=5000L, Multiplier =2, then the first retry is 5 seconds, the second is 10 seconds, and the third is 20 seconds
- Retryable specifies the annotation parameter
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Retryable {
/**
* Retry interceptor bean name to be applied for retryable method. Is mutually
* exclusive with other attributes.
* @return the retry interceptor bean name
*/
String interceptor(a) default "";
/** * Exception types that are retryable. Synonym for includes(). Defaults to empty (and * if excludes is also empty all exceptions are retried). *@return exception types to retry
*/
Class<? extends Throwable>[] value() default {};
/**
* Exception types that are retryable. Defaults to empty (and if excludes is also
* empty all exceptions are retried).
* @return exception types to retry
*/
Class<? extends Throwable>[] include() default {};
/**
* Exception types that are not retryable. Defaults to empty (and if includes is also
* empty all exceptions are retried).
* If includes is empty but excludes is not, all not excluded exceptions are retried
* @return exception types not to retry
*/
Class<? extends Throwable>[] exclude() default {};
/**
* A unique label for statistics reporting. If not provided the caller may choose to
* ignore it, or provide a default.
*
* @return the label for the statistics
*/
String label(a) default "";
/**
* Flag to say that the retry is stateful: i.e. exceptions are re-thrown, but the
* retry policy is applied with the same policy to subsequent invocations with the
* same arguments. If false then retryable exceptions are not re-thrown.
* @return true if retry is stateful, default false
*/
boolean stateful(a) default false;
/ * * *@return the maximum number of attempts (including the first failure), defaults to 3
*/
int maxAttempts(a) default 3;
/ * * *@return an expression evaluated to the maximum number of attempts (including the first failure), defaults to 3
* Overrides {@link #maxAttempts()}.
* @date1.2 * /
String maxAttemptsExpression(a) default "";
/**
* Specify the backoff properties for retrying this operation. The default is a
* simple {@link Backoff} specification with no properties - see it's documentation
* for defaults.
* @return a backoff specification
*/
Backoff backoff(a) default @Backoff(a);
/**
* Specify an expression to be evaluated after the {@code SimpleRetryPolicy.canRetry()}
* returns true - can be used to conditionally suppress the retry. Only invoked after
* an exception is thrown. The root object for the evaluation is the last {@code Throwable}.
* Other beans in the context can be referenced.
* For example:
* <pre class=code>
* {@code "message.contains('you can retry this')"}.
* </pre>
* and
* <pre class=code>
* {@code "@someBean.shouldRetry(#root)"}.
* </pre>
* @return the expression.
* @date1.2 * /
String exceptionExpression(a) default "";
/**
* Bean names of retry listeners to use instead of default ones defined in Spring context
* @return retry listeners bean names
*/
String[] listeners() default {};
}
Copy the code
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Backoff {
/**
* Synonym for {@link #delay()}.
*
* @return the delay in milliseconds (default 1000)
*/
long value(a) default 1000;
/**
* A canonical backoff period. Used as an initial value in the exponential case, and
* as a minimum value in the uniform case.
* @return the initial or canonical backoff period in milliseconds (default 1000)
*/
long delay(a) default 0;
/**
* The maximimum wait (in milliseconds) between retries. If less than the
* {@link #delay()} then the default of
* {@value org.springframework.retry.backoff.ExponentialBackOffPolicy#DEFAULT_MAX_INTERVAL}
* is applied.
*
* @return the maximum delay between retries (default 0 = ignored)
*/
long maxDelay(a) default 0;
/**
* If positive, then used as a multiplier for generating the next delay for backoff.
*
* @return a multiplier to use to calculate the next backoff delay (default 0 =
* ignored)
*/
double multiplier(a) default 0;
/**
* An expression evaluating to the canonical backoff period. Used as an initial value
* in the exponential case, and as a minimum value in the uniform case. Overrides
* {@link #delay()}.
* @return the initial or canonical backoff period in milliseconds.
* @date1.2 * /
String delayExpression(a) default "";
/**
* An expression evaluating to the maximimum wait (in milliseconds) between retries.
* If less than the {@link #delay()} then the default of
* {@value org.springframework.retry.backoff.ExponentialBackOffPolicy#DEFAULT_MAX_INTERVAL}
* is applied. Overrides {@link #maxDelay()}
*
* @return the maximum delay between retries (default 0 = ignored)
* @date1.2 * /
String maxDelayExpression(a) default "";
/**
* Evaluates to a vaule used as a multiplier for generating the next delay for
* backoff. Overrides {@link #multiplier()}.
*
* @return a multiplier expression to use to calculate the next backoff delay (default
* 0 = ignored)
* @date1.2 * /
String multiplierExpression(a) default "";
/**
* In the exponential case ({@link #multiplier()} > 0) set this to true to have the
* backoff delays randomized, so that the maximum delay is multiplier times the
* previous delay and the distribution is uniform between the two values.
*
* @return the flag to signal randomization is required (default false)
*/
boolean random(a) default false;
}
Copy the code
- Set the retry times, retry exception type, rollback delay, retry policy, and method listening name for the method to be retried
@Component
public class PlatformClassService {
@retryable (// value = {Exception. Class}, // maxAttempts = 5, // Set backoff = @backoff (delay = 500), // Set callback method name to listeners = "retryListener")
public void call(a) {
System.out.println("call...");
throw new RuntimeException("Manual exception"); }}Copy the code
// Initial delay of 2 seconds, and then acceptance 1.5 times the delay retry, total retry times 4
@retryable (value = {Exception. Class}, maxAttempts = 4, backoff = @backoff (delay = 2000L, Multiplier = 1.5))
Copy the code
- The listening method is configured in the configuration class
/** * annotation call */
@Bean
public RetryListener retryListener(a) {
return new RetryListener() {
@Override
public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
System.out.println("open context = " + context + ", callback = " + callback);
// Return true to continue with subsequent calls
return true;
}
@Override
public <T, E extends Throwable> void close(RetryContext context, RetryCallback
callback, Throwable throwable)
,> {
System.out.println("close context = " + context + ", callback = " + callback);
}
@Override
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback
callback, Throwable throwable)
,> {
System.out.println("onError context = " + context + ", callback = "+ callback); }}; }Copy the code
- Call the service
@RestController
public class SpringRetryController {
@Resource
private PlatformClassService platformClassService;
@RequestMapping("/retryPlatformCall")
public Object retryPlatformCall(a) {
try {
platformClassService.call();
} catch (Exception e) {
return "Failed attempt to invoke";
}
return "retryPlatformCall"; }}Copy the code
- The results