有些场景需要我们对一些异常情况下面的任务进行重试,比如:调用远程的RPC服务,可能由于网络抖动出现第一次调用失败,尝试几次就可以恢复正常。

spring-retry是spring提供的一个基于spring的重试框架,非常好用。

官网地址: https://github.com/spring-projects/spring-retry

下面是springboot调用spring-retry:

配置依赖Maven:

<dependency><groupId>org.springframework.retry</groupId><artifactId>spring-retry</artifactId>
</dependency>

启动类:

@SpringBootApplication
@EnableTransactionManagement // 启注解事务管理
@EnableScheduling
@EnableRetry // 【注意这里】启用了重试功能
public class TestWebApplication {public static void main(String[] args) {SpringApplication.run(TestWebApplication.class, args);}
}

重试部分的代码:

@Service
public class RetryServiceImpl implements RetryService {private static final Logger LOGGER = LoggerFactory.getLogger(RetryServiceImpl.class);private AtomicInteger count = new AtomicInteger(1);@Override@Retryable(value = { RemoteAccessException.class }, maxAttemptsExpression = "${retry.maxAttempts:10}",backoff = @Backoff(delayExpression = "${retry.backoff:1000}"))public void retry() {LOGGER.info("start to retry : " + count.getAndIncrement());throw new RemoteAccessException("here " + count.get());}@Recoverpublic void recover(RemoteAccessException t) {LOGGER.info("SampleRetryService.recover:{}", t.getClass().getName());}
}    

【注意】@Recover 的用法。它要求它注释的方法的返回值必须和@Retryable的注释的方法返回值保持一致,否则@Recover 注释的方法不会被调用。它还有关于自己参数的使用要求。

更详细的@Recover 的使用说明,参考它的官网Javadoc,如下:

原文 写道
Recovery method can be supplied, in case you want to take an alternative code path when the retry is exhausted. Methods should be declared in the same class as the @Retryable and marked @Recover. The return type must match the @Retryable method. The arguments for the recovery method can optionally include the exception that was thrown, and also optionally the arguments passed to the orginal retryable method (or a partial list of them as long as none are omitted).
org.springframework.retry.annotation.Recover@Import(value={RetryConfiguration.class})
@Target(value={METHOD, TYPE})
@Retention(value=RUNTIME)
@Documented
Annotation for a method invocation that is a recovery handler.
A suitable recovery handler has a first parameter of
type Throwable (or a subtype of Throwable)
and a return value of the same type as the @Retryable
method to recover from. The Throwable first argument
is optional (but a method without it will only
be called if no others match). Subsequent arguments
are populated from the argument list of the failed method in order.Since:
2.0
Author:
Dave Syer

还可以自己写代码的方法定制化retry的使用,如下:

    public static void main(String[] args) throws InterruptedException {RetryTemplate template = new RetryTemplate();TimeoutRetryPolicy policy = new TimeoutRetryPolicy();policy.setTimeout(2000L);SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();retryPolicy.setMaxAttempts(5);//template.setRetryPolicy(policy);template.setRetryPolicy(retryPolicy);String result = template.execute(context -> {System.out.println("TestAll.main()1");TimeUnit.SECONDS.sleep(1L);throw new IllegalArgumentException();}, context -> {System.out.println("TestAll.main()2");return "world";});System.out.println("result:" + result);}

【注意】

interface RetryPolicy 它有很多的实现类可以使用。

下面是官网的原始内容:

This project provides declarative retry support for Spring applications. It is used in Spring Batch, Spring Integration, Spring for Apache Hadoop (amongst others).

Quick Start

Example:

@Configuration
@EnableRetry
public class Application {@Beanpublic Service service() {return new Service();}}@Service
class Service {@Retryable(RemoteAccessException.class)public void service() {// ... do something}@Recoverpublic void recover(RemoteAccessException e) {// ... panic}
}

Call the "service" method and if it fails with a RemoteAccessException then it will retry (up to three times by default), and then execute the "recover" method if unsuccessful. There are various options in the @Retryable annotation attributes for including and excluding exception types, limiting the number of retries and the policy for backoff.

Building

Requires Java 1.7 and Maven 3.0.5 (or greater)

$ mvn install

Features and API

RetryTemplate

To make processing more robust and less prone to failure, sometimes it helps to automatically retry a failed operation in case it might succeed on a subsequent attempt. Errors that are susceptible to this kind of treatment are transient in nature. For example a remote call to a web service or RMI service that fails because of a network glitch or a DeadLockLoserExceptionin a database update may resolve themselves after a short wait. To automate the retry of such operations Spring Retry has the RetryOperations strategy. The RetryOperations interface looks like this:

public interface RetryOperations {<T> T execute(RetryCallback<T> retryCallback) throws Exception;<T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback)throws Exception;<T> T execute(RetryCallback<T> retryCallback, RetryState retryState)throws Exception, ExhaustedRetryException;<T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback,RetryState retryState) throws Exception;}

The basic callback is a simple interface that allows you to insert some business logic to be retried:

public interface RetryCallback<T> {T doWithRetry(RetryContext context) throws Throwable;}

The callback is executed and if it fails (by throwing an Exception), it will be retried until either it is successful, or the implementation decides to abort. There are a number of overloaded execute methods in the RetryOperations interface dealing with various use cases for recovery when all retry attempts are exhausted, and also with retry state, which allows clients and implementations to store information between calls (more on this later).

The simplest general purpose implementation of RetryOperations is RetryTemplate. It could be used like this

RetryTemplate template = new RetryTemplate();TimeoutRetryPolicy policy = new TimeoutRetryPolicy();
policy.setTimeout(30000L);template.setRetryPolicy(policy);Foo result = template.execute(new RetryCallback<Foo>() {public Foo doWithRetry(RetryContext context) {// Do stuff that might fail, e.g. webservice operationreturn result;}});

In the example we execute a web service call and return the result to the user. If that call fails then it is retried until a timeout is reached.

RetryContext

The method parameter for the RetryCallback is a RetryContext. Many callbacks will simply ignore the context, but if necessary it can be used as an attribute bag to store data for the duration of the iteration.

RetryContext will have a parent context if there is a nested retry in progress in the same thread. The parent context is occasionally useful for storing data that need to be shared between calls to execute.

RecoveryCallback

When a retry is exhausted the RetryOperations can pass control to a different callback, the RecoveryCallback. To use this feature clients just pass in the callbacks together to the same method, for example:

Foo foo = template.execute(new RetryCallback<Foo>() {public Foo doWithRetry(RetryContext context) {// business logic here},new RecoveryCallback<Foo>() {Foo recover(RetryContext context) throws Exception {// recover logic here}
});

If the business logic does not succeed before the template decides to abort, then the client is given the chance to do some alternate processing through the recovery callback.

Stateless Retry

In the simplest case, a retry is just a while loop: the RetryTemplate can just keep trying until it either succeeds or fails. The RetryContext contains some state to determine whether to retry or abort, but this state is on the stack and there is no need to store it anywhere globally, so we call this stateless retry. The distinction between stateless and stateful retry is contained in the implementation of the RetryPolicy (the RetryTemplate can handle both). In a stateless retry, the callback is always executed in the same thread on retry as when it failed.

Stateful Retry

Where the failure has caused a transactional resource to become invalid, there are some special considerations. This does not apply to a simple remote call because there is no transactional resource (usually), but it does sometimes apply to a database update, especially when using Hibernate. In this case it only makes sense to rethrow the exception that called the failure immediately so that the transaction can roll back and we can start a new valid one.

In these cases a stateless retry is not good enough because the re-throw and roll back necessarily involve leaving the RetryOperations.execute() method and potentially losing the context that was on the stack. To avoid losing it we have to introduce a storage strategy to lift it off the stack and put it (at a minimum) in heap storage. For this purpose Spring Retry provides a storage strategy RetryContextCache which can be injected into the RetryTemplate. The default implementation of the RetryContextCache is in memory, using a simple Map. It has a strictly enforced maximum capacity, to avoid memory leaks, but it doesn't have any advanced cache features like time to live. You should consider injecting a Map that had those features if you need them. Advanced usage with multiple processes in a clustered environment might also consider implementing the RetryContextCache with a cluster cache of some sort (though, even in a clustered environment this might be overkill).

Part of the responsibility of the RetryOperations is to recognize the failed operations when they come back in a new execution (and usually wrapped in a new transaction). To facilitate this, Spring Retry provides the RetryState abstraction. This works in conjunction with a special execute methods in the RetryOperations.

The way the failed operations are recognized is by identifying the state across multiple invocations of the retry. To identify the state, the user can provide an RetryState object that is responsible for returning a unique key identifying the item. The identifier is used as a key in the RetryContextCache.

Warning: Be very careful with the implementation of Object.equals() and Object.hashCode() in the key returned by RetryState. The best advice is to use a business key to identify the items. In the case of a JMS message the message ID can be used.

When the retry is exhausted there is also the option to handle the failed item in a different way, instead of calling the RetryCallback (which is presumed now to be likely to fail). Just like in the stateless case, this option is provided by the RecoveryCallback, which can be provided by passing it in to the execute method of RetryOperations.

The decision to retry or not is actually delegated to a regular RetryPolicy, so the usual concerns about limits and timeouts can be injected there (see below).

Retry Policies

Inside a RetryTemplate the decision to retry or fail in the execute method is determined by a RetryPolicy which is also a factory for the RetryContext. The RetryTemplate has the responsibility to use the current policy to create a RetryContextand pass that in to the RetryCallback at every attempt. After a callback fails the RetryTemplate has to make a call to the RetryPolicy to ask it to update its state (which will be stored in the RetryContext), and then it asks the policy if another attempt can be made. If another attempt cannot be made (e.g. a limit is reached or a timeout is detected) then the policy is also responsible for identifying the exhausted state, but not for handling the exception. The RetryTemplate will throw the original exception, except in the stateful case, when no recover is available, in which case it throws RetryExhaustedException. You can also set a flag in the RetryTemplate to have it unconditionally throw the original exception from the callback (i.e. from user code) instead.

Tip: Failures are inherently either retryable or not - if the same exception is always going to be thrown from the business logic, it doesn't help to retry it. So don't retry on all exception types - try to focus on only those exceptions that you expect to be retryable. It's not usually harmful to the business logic to retry more aggressively, but it's wasteful because if a failure is deterministic there will be time spent retrying something that you know in advance is fatal.

Spring Retry provides some simple general purpose implementations of stateless RetryPolicy, for example a SimpleRetryPolicy, and the TimeoutRetryPolicy used in the example above.

The SimpleRetryPolicy just allows a retry on any of a named list of exception types, up to a fixed number of times:

// Set the max attempts including the initial attempt before retrying
// and retry on all exceptions (this is the default):
SimpleRetryPolicy policy = new SimpleRetryPolicy(5, Collections.singletonMap(Exception.class, true));// Use the policy...
RetryTemplate template = new RetryTemplate();
template.setRetryPolicy(policy);
template.execute(new RetryCallback<Foo>() {public Foo doWithRetry(RetryContext context) {// business logic here}
});

There is also a more flexible implementation called ExceptionClassifierRetryPolicy, which allows the user to configure different retry behavior for an arbitrary set of exception types though the ExceptionClassifier abstraction. The policy works by calling on the classifier to convert an exception into a delegate RetryPolicy, so for example, one exception type can be retried more times before failure than another by mapping it to a different policy.

Users might need to implement their own retry policies for more customized decisions. For instance, if there is a well-known, solution-specific, classification of exceptions into retryable and not retryable.

Backoff Policies

When retrying after a transient failure it often helps to wait a bit before trying again, because usually the failure is caused by some problem that will only be resolved by waiting. If a RetryCallback fails, the RetryTemplate can pause execution according to the BackoffPolicy in place.

public interface BackoffPolicy {BackOffContext start(RetryContext context);void backOff(BackOffContext backOffContext)throws BackOffInterruptedException;}

BackoffPolicy is free to implement the backOff in any way it chooses. The policies provided by Spring Retry out of the box all use Object.wait(). A common use case is to backoff with an exponentially increasing wait period, to avoid two retries getting into lock step and both failing - this is a lesson learned from the ethernet. For this purpose Spring Retry provides the ExponentialBackoffPolicy. There are also randomized versions delay policies that are quite useful to avoid resonating between related failures in a complex system.

Listeners

Often it is useful to be able to receive additional callbacks for cross cutting concerns across a number of different retries. For this purpose Spring Retry provides the RetryListener interface. The RetryTemplate allows users to register RetryListeners, and they will be given callbacks with the RetryContext and Throwable where available during the iteration.

The interface looks like this:

public interface RetryListener {void open(RetryContext context, RetryCallback<T> callback);void onError(RetryContext context, RetryCallback<T> callback, Throwable e);void close(RetryContext context, RetryCallback<T> callback, Throwable e);
}

The open and close callbacks come before and after the entire retry in the simplest case and onError applies to the individual RetryCallback calls. The close method might also receive a Throwable; if there has been an error it is the last one thrown by the RetryCallback.

Note that when there is more than one listener, they are in a list, so there is an order. In this case open will be called in the same order while onError and close will be called in reverse order.

Declarative Retry

Sometimes there is some business processing that you know you want to retry every time it happens. The classic example of this is the remote service call. Spring Retry provides an AOP interceptor that wraps a method call in a RetryOperationsfor just this purpose. The RetryOperationsInterceptor executes the intercepted method and retries on failure according to the RetryPolicy in the provided RepeatTemplate.

Java Configuration for Retry Proxies

Add the @EnableRetry annotation to one of your @Configuration classes and use @Retryable on the methods (or type level for all methods) that you want to retry. You can also specify any number of retry listeners. Example

@Configuration
@EnableRetry
public class Application {@Beanpublic Service service() {return new Service();}@Bean public RetryListener retryListener1() {return new RetryListener() {...}}@Bean public RetryListener retryListener2() {return new RetryListener() {...}}}@Service
class Service {@Retryable(RemoteAccessException.class)public service() {// ... do something}
}

Attributes of @Retryable can be used to control the RetryPolicy and BackoffPolicy, e.g.

@Service
class Service {@Retryable(maxAttempts=12, backoff=@Backoff(delay=100, maxDelay=500))public service() {// ... do something}
}

for a random backoff between 100 and 500 milliseconds and up to 12 attempts. There is also a stateful attribute (default false) to control whether the retry is stateful or not. To use stateful retry the intercepted method has to have arguments, since they are used to construct the cache key for the state.

The @EnableRetry annotation also looks for beans of type Sleeper and other strategies used in the RetryTemplate and interceptors to control the beviour of the retry at runtime.

The @EnableRetry annotation creates proxies for @Retryable beans, and the proxies (so the bean instances in the application) have the Retryable interface added to them. This is purely a marker interface, but might be useful for other tools looking to apply retry advice (they should usually not bother if the bean already implements Retryable).

Recovery method can be supplied, in case you want to take an alternative code path when the retry is exhausted. Methods should be declared in the same class as the @Retryable and marked @Recover. The return type must match the @Retryable method. The arguments for the recovery method can optionally include the exception that was thrown, and also optionally the arguments passed to the orginal retryable method (or a partial list of them as long as none are omitted). Example:

@Service
class Service {@Retryable(RemoteAccessException.class)public void service(String str1, String str2) {// ... do something}@Recoverpublic void recover(RemoteAccessException e, String str1, String str2) {// ... error handling making use of original args if required}
}

Version 1.2 introduces the ability to use expressions for certain properties:

@Retryable(exceptionExpression="#{message.contains('this can be retried')}")
public void service1() {...
}@Retryable(exceptionExpression="#{message.contains('this can be retried')}")
public void service2() {...
}@Retryable(exceptionExpression="#{@exceptionChecker.shouldRetry(#root)}",maxAttemptsExpression = "#{@integerFiveBean}",backoff = @Backoff(delayExpression = "#{1}", maxDelayExpression = "#{5}", multiplierExpression = "#{1.1}"))
public void service3() {...
}

These use the familier Spring SpEL expression syntax (#{...}).

Expressions can contain property placeholders such as #{${max.delay}} or #{@exceptionChecker.${retry.method}(#root)}

  • exceptionExpression is evaluated against the thrown exception as the #root object.
  • maxAttemptsExpression and the @BackOff expression attributes are evaluated once, during initialization; there is no root object for the evaluation but they can reference other beans in the context.

XML Configuration

Here is an example of declarative iteration using Spring AOP to repeat a service call to a method called remoteCall (for more detail on how to configure AOP interceptors see the Spring User Guide):

<aop:config><aop:pointcut id="transactional"expression="execution(* com..*Service.remoteCall(..))" /><aop:advisor pointcut-ref="transactional"advice-ref="retryAdvice" order="-1"/>
</aop:config><bean id="retryAdvice"class="org.springframework.retry.interceptor.RetryOperationsInterceptor"/>

The example above uses a default RetryTemplate inside the interceptor. To change the policies or listeners, you only need to inject an instance of RetryTemplate into the interceptor.

Contributing

Spring Retry is released under the non-restrictive Apache 2.0 license, and follows a very standard Github development process, using Github tracker for issues and merging pull requests into master. If you want to contribute even something trivial please do not hesitate, but follow the guidelines below.

Before we accept a non-trivial patch or pull request we will need you to sign the https://cla.pivotal.io/[contributor'sagreement]. Signing the contributor's agreement does not grant anyone commit rights to the main repository, but it does mean that we can accept your contributions, and you will get an author credit if we do. Active contributors might be asked to join the core team, and given the ability to merge pull requests.

Code of Conduct

This project adheres to the Contributor Covenant. By participating, you are expected to uphold this code. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.

spring-retry简单用法相关推荐

  1. 高级JAVA - 手写简单的重试组件学习Spring Retry

    目录 一 . 定义注解 二 . 利用cglib代理扩展重试业务 三 . 编写代理类 , 使用自定义的XRetryInterceptor作为拦截器 四 . 编写相关业务方法 , 测试代码 五 . 测试结 ...

  2. Spring AOP 简介以及简单用法

    Spring AOP 简介以及简单用法 如果你去面试java开发, 那么Spring的AOP和DI几乎是必问的问题. 那么AOP是什么呢? 一. AOP 所谓Aop就是 Aspect-Oriented ...

  3. Spring常用注解,以及注解的作用和简单用法

    这篇文章主要介绍了Spring当中的常见的一些注解,以及注解的简单用法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 @Configuration 作用:指定当 ...

  4. foxmail邮件加载失败重试_java retry(重试) spring retry, guava retrying 详解

    系列说明 java retry 的一步步实现机制. java-retry 源码地址 情景导入 简单的需求 产品经理:实现一个按条件,查询用户信息的服务. 小明:好的.没问题. 代码 UserServi ...

  5. java retry(重试) spring retry, guava retrying 详解

    转载 自 http://blog.51cto.com/9250070/2156431 系列说明 java retry 的一步步实现机制. java-retry 源码地址 情景导入 简单的需求 产品经理 ...

  6. 针对Spring的Spring Retry 我发现了这样一个大家都不知道的技巧!

    外部服务对于调用者来说一般都是不可靠的,尤其是在网络环境比较差的情况下,网络抖动很容易导致请求超时等异常情况,这时候就需要使用失败重试策略重新调用 API 接口来获取.重试策略在服务治理方面也有很广泛 ...

  7. Spring Retry 重试机制实现及原理

    概要 Spring实现了一套重试机制,功能简单实用.Spring Retry是从Spring Batch独立出来的一个功能,已经广泛应用于Spring Batch,Spring Integration ...

  8. Spring异常重试框架Spring Retry

    在调用第三方接口或者使用mq时,会出现网络抖动,连接超时等网络异常,所以需要重试.为了使处理更加健壮并且不太容易出现故障,后续的尝试操作,有时候会帮助失败的操作最后执行成功.例如,由于网络故障或数据库 ...

  9. Spring 异步@Async注解用法 Spring @Async注解用法总结 Spring @Async基本用法示例

    Spring 异步@Async注解用法 Spring @Async注解用法总结 Spring @Async基本用法示例 一.概述 在日常开发的工作中,经常会使用异步进行开发.Spring 提供一个简单 ...

最新文章

  1. 【Interfacenavigation】通知概述(36)
  2. 五分钟学会HTML5!(二)
  3. 什么叫轻量瓷_为什么说陶瓷是华夏文明的徽章?
  4. 【Leetcode | 47】 222. 完全二叉树的节点个数
  5. mybatis oracle 大写,解决mybatis用Map返回的字段全变大写的问题
  6. UVA 620 - Cellular Structure
  7. getBoundingClientRect使用指南
  8. Pandas 中的这 3 个函数,没想到竟成了我数据处理的主力
  9. 【论文写作】毕业论文写作必备技巧:修改文章的诀窍
  10. DML DDL DCL区别
  11. 如果REST应用程序应该是无状态的,那么如何管理会话?
  12. 03-4 BGP 默认路由/MED
  13. html怎么用pdf保存,html保存为PDF
  14. 用Maven构建 Fat JAR
  15. 计算机未显示移动硬盘,电脑不显示移动硬盘怎么办_移动硬盘已连接不显示解决教程...
  16. java观察者模式举例_写个观察者模式(Observer Pattern)的例子
  17. C# 获取电脑序列号和主板序列号
  18. 微信小程序实现微信APP上的扫一扫扫码跳到小程序对应的结果页面和签字等功能
  19. 华为机试在线训练-牛客网(20)【中级】字符串运用-密码截取
  20. PMM--简介与部署

热门文章

  1. LabVIEW使用两三年感触
  2. 什么是深度学习以及它是如何工作的?
  3. 为什么将指令cache和数据cache相分离?
  4. 拿到CCIE证书两年后
  5. 写论文难,提纲该怎么写?
  6. cmake 超简单使用
  7. .NET Framework、 .NET Core、.NET Standard区别。
  8. delphi html 表格控件,Delphi之TMS开发组件包-TAdvStringGrid表格控件
  9. 俄罗斯自研8核CPU性能实测:能玩几十年前老游戏,下一代「高性能」芯片无人代工...
  10. Linux 中 4 款炫酷的终端应用程序