Spring retry is an energy function independent from spring batch, which mainly implements retry and fuse. There are scenario restrictions for retry, and not all scenarios are suitable for retry, such as illegal parameter verification, write operations, etc. (you need to consider whether the write is idempotent) are not suitable for retry. Remote call timeout or network interruption can be retryed. In the microservice governance framework, it usually has its own retry and timeout configuration. For example, dubbo can set retries=1, timeout=500 call fails and retry only once, and call fails if it does not return after more than 500ms. In spring retry you can specify the type of exception that needs to be retryed, and set the interval for each retry and if the retry fails, whether to continue retry or fuse (stop retry).
Design and implementation
RetryOperations defines the retry API. RetryTemplate is the template mode implementation of the API, which implements retry and circuit breaking. The provided API is as follows:
public interface RetryOperations { <T, E extends Throwable>T execute(RetryCallback<T, E>retryCallback) throws E; } // Other APIs have been omitted RetryCallback defines the operation that needs to be performed. After defining the operation, it is the question of how to retry. RetryTemplate executes the logic of how to retry by formulating different retry strategies. The default retry strategy is SimpleRetryPlicy , which means it will be retryed 3 times. If the retry is successful, you will not continue to try again. So what if the 3 feet retry failed? The process ends or returns the bottom-up result. To return the bottom-up result, you need to configure RecoveyCallBack . From the name, you can see that this is a bottom-up callback interface, which is the logic of execution after the retry failed. In addition to SimpleRetryPolicy , there are other retry strategies. Let’s take a look at the RetryPolicy interface:
public interface RetryPolicy extends Serializable { boolean canRetry(RetryContext context); RetryContext open(RetryContext parent); void close(RetryContext context); void registerThrowable(RetryContext context, Throwable throwable);} canRetry is called every time you try again. The judgment condition of whether you can continue to try again
Called before the open retry starts, a retry context will be created to RetryContext , and the retry stack will be saved.
registerThrowable is called every time the exception is retryed (there is an exception and it will continue to retry)
Take SimpleRetryPolicy as an example. When the number of retrys reaches 3 (default 3 times), stop retrying, and the number of retrying is saved in the retry context.
Provide the following retry strategy implementation:
Retry the fallback strategy refers to whether each retry is retry immediately or wait for a while before trying again. By default, you need to specify the backoff policy BackoffRetryPolicy if you need to configure a waiting period and then try again. BackoffRetryPolicy has the following implementation:
Stateful retry OR Stateless retry
The so-called stateless retry refers to retry completed in a thread context. Otherwise, if retry not completed in a thread context is stateful retry. The previous SimpleRetryPolicy was a stateless retry because retry was completed in a loop. So what happens after that or need to be retryed in a state? There are usually two situations: transaction rollback and circuit breaker.
DataAccessException is exceptional. Retry cannot be performed, but if other exceptions are thrown, you can try again.
Fuse means not to handle retry in the current loop, but to global retry mode (not thread context). The circuit breaker will jump out of the loop, and the stack information of the thread context will inevitably be lost. Then it is definitely necessary to save this information in a "global mode". The current implementation is placed in a cache (map implementation). You can continue to try again after getting it from the cache next time.
Quick Start
Use @EnableRetry on the class that needs to perform retry, if proxyTargetClass=true is set, this uses CGLIB dynamic proxy:
@Configuration@EnableRetry(proxyTargetClass = true)@Componentpublic class RetryExamples {} Retry based on the maximum number of retries strategy, if the retry is repeated 3 times and the exception is still thrown, the retry is stopped and the bottom-up callback is executed. Therefore, the final output result is Integer.MAX_VALUE :
private void retryExample3() throws Exception { RetryTemplate retryTemplate = new RetryTemplate(); SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy(); simpleRetryPolicy.setMaxAttempts(3); retryTemplate.setRetryPolicy(simpleRetryPolicy); Integer result = retryTemplate.execute(new RetryCallback<Integer, Exception>() { int i = 0; // Retry the operation @Override public Integer doWithRetry(RetryContext retryContext) throws Exception { log.info("retry count: {}", retryContext.getRetryCount()); return len(i++); } }, new RecoveryCallback<Integer>() { //Base callback @Override public Integer recover(RetryContext retryContext) throws Exception { log.info("after retry: {}, recovery method called!", retryContext.getRetryCount()); return Integer.MAX_VALUE; } }); log.info("final result: {}", result); } private int len(int i) throws Exception { if (i < 10) throw new Exception(i + " le 10"); return i; }The following describes how to use the circuit breaker retry policy mode (CircuitBreakerRetryPolicy). The following three parameters need to be set:
Circuit breaker opening and closing judgment:
The test code is as follows:
RetryTemplate template = new RetryTemplate(); CircuitBreakerRetryPolicy retryPolicy = new CircuitBreakerRetryPolicy(new SimpleRetryPolicy(3)); retryPolicy.setOpenTimeout(5000); retryPolicy.setResetTimeout(20000); template.setRetryPolicy(retryPolicy); for (int i = 0; i < 10; i++) { //Thread.sleep(100); try { Object key = "circuit"; boolean isForceRefresh = false; RetryState state = new DefaultRetryState(key, isForceRefresh); String result = template.execute(new RetryCallback<String, RuntimeException>() { @Override public String doWithRetry(RetryContext context) throws RuntimeException { log.info("retry count: {}", context.getRetryCount()); throw new RuntimeException("timeout"); } }, new RecoveryCallback<String>() { @Override public String recover(RetryContext context) throws Exception { return "default"; } }, state); log.info("result: {}", result); } catch (Exception e) { System.out.println(e); } } Since isForceRefresh = false is set, the value of key = "circuit" (that is, RetryContext) will be obtained from the cache. Therefore, when the retry fails and this.time < this.openWindow is fused, you can still continue to implement retry in the global mode ( RetryContext obtained is the same).
Annotation development
If writing a RetryTemplate every time you have a retry requirement is too bloated, using annotations can greatly simplify development and reduce duplicate code. Here is a retry of the maximum retry strategy implemented using annotations:
@Retryable(value = SQLDataException.class, backoff = @Backoff(value = 0L)) public String service3() throws SQLDataException { log.info("service3 open"); throw new SQLDataException(); } @Recover public String recover(SQLDataException ne) { return "SQLDataException recover"; }Annotations include:
@EnableRetry
@Retryable
@Recover
@Backoff
@CircuitBreaker
@EnableRetry: Can you try again? When the proxyTargetClass property is true (default false), use CGLIB proxy
@Retryable: The method that annotation needs to be retryed
@Backoff: Retry the fallback strategy (try now or wait for a while before trying again)
@Recover: for use with methods. Used as a "guaranteed" method when @Retryable fails. The method of @Recover annotation must be consistent with the method "signature" of @Retryable annotation. The first entry parameter is the exception to be retryed. Other parameters are consistent with @Retryable. The return value must be the same, otherwise it cannot be executed!
@CircuitBreaker: used for method, implement circuit breaking mode.
For more examples, welcome to my Github(https://github.com/happyxiaofan/springboot-learning-example) star. Thanks
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.