由于场景需求,需要对项目依赖的部分外部接口添加响应超时重试机制,而Spring Retry可以满足该需求,因此进行相关资料查询。对于Spring Retry的使用,网上的资料主要是采用注解的方式实现,而对于依赖的外部接口的重试,为避免对将重试机制添加到业务逻辑中,优先采用xml配置的方式实现。本文主要对https://www.baeldung.com/spring-retry中的教程进行翻译。
Spring Retry提供了自动调用失败操作的功能,这有助于解决瞬时错误(如网络抖动)。Spring Retry提供对流程和基于策略行为的声明式控制,易于扩展和自定义。
本文主要介绍Spring Retry在Spring项目中实现重试机制,同时也会配置监听者获取额外的回调结果。
Maven坐标
org.springframework.retry
spring-retry
1.2.4.RELEASE
可以在https://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22org.springframework.retry%22%20AND%20a%3A%22spring-retry%22查看Retry的最新版本
使能Spring Retry
可以通过两种方式使能Spring Retry。
1、@EnableRetry注解方式
@Configuration @EnableRetry public class AppConfig { ... }
2、xml文件配置
Retry实现方式
Retry的用法有三种,Annotation形式、RetryTemplate形式、XML形式。
注解方式
1、@Retryable
将@Retryable添加在需要重试的方法,即retryService()。
@Service public class MyService { @Retryable( value = { SQLException.class }, maxAttempts = 2, backoff = @Backoff(delay = 5000)) void retryService(String sql) throws SQLException{...} ... } /* value:抛出指定异常才会重试 include:和value一样,默认为空,当exclude也为空时,默认所以异常 exclude:指定不处理的异常 maxAttempts:最大重试次数,默认3次 backoff:重试等待策略,默认使用@Backoff,@Backoff的value默认为1000L,我们设置为2000L;multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5秒。 */
上面的示例中,在方法出现SQLException异常时,将会重试2次,每次间隔5000ms。如果@Retryable没有任何说明,默认在方法抛出异常时,将会重试3次,每次间隔1s。
2、@Recover
在@Retryable方法重试后依然失败,调用@Recover定义的一个单独恢复方法。
@Service public class MyService { ... @Recover void recover(SQLException e, String sql){...} }
如果retryService()方法抛出SQLException,将会调用recover()方法。合适的恢复处理程序的第一个参数类型为Throwable(可选)。 后续参数从失败方法的参数列表中填充,其顺序与失败方法的顺序相同,并且具有相同的返回类型。
RetryTemplate方式
1、RetryOperations
Spring Retry提供RetryOperations接口,提供一系列的execute()方法:
public interface RetryOperations {
T execute(RetryCallback
retryCallback) throws Exception; ... }
其中,参数RetryCallback是在业务逻辑失败后执行的接口:
public interface RetryCallback
{ T doWithRetry(RetryContext context) throws Throwable; }
2、RetryTemplate配置
RetryTemplate是RetryOperations的实现类。在@Configuration类中可以配置RetryTemplate:
@Configuration public class AppConfig { //... @Bean public RetryTemplate retryTemplate() { RetryTemplate retryTemplate = new RetryTemplate(); FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); fixedBackOffPolicy.setBackOffPeriod(2000l); retryTemplate.setBackOffPolicy(fixedBackOffPolicy); SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(); retryPolicy.setMaxAttempts(2); retryTemplate.setRetryPolicy(retryPolicy); return retryTemplate; } }
RetryPolicy接口确定方法重试的规则。SimpleRetryPolicy类用于设置失败方法的固定重试次数。
BackOffPolicy接口用于控制不同的重试的间隔。FixedBackOffPolicy类设置失败方法以固定的时间间隔进行重试。
3、应用RetryTemplate
通过调用retryTemplate.execute()使方法具有重试功能:
retryTemplate.execute(new RetryCallback
() { @Override public Void doWithRetry(RetryContext arg0) { myService.templateRetryService(); ... } });
其中,templateRetryService()为需要调用失败后重试的方法。
可以采用lambda表达式代替匿名类:
retryTemplate.execute(arg0 -> { myService.templateRetryService(); return null; });
XML方式
Spring Retry可以通过xml使用Spring AOP命名空间配置。
1、添加xml文件
在classpath中添加retryadvic.xml。
这是通过xml配置了RetryTemplate。其中,MyService.xmlRetryService()为需要失败后重试的方法。retryPolicy定义重试次数和捕获的异常。backOffPolicy定义不同重试间的时间间隔等。(如上文的FixedBackOffPolicy类,ExponentialBackOffPolicy类未深入研究)
2、xml配置的应用
导入retryadvice.xml到classpath,并使能@AspectJ:
@Configuration @EnableRetry @EnableAspectJAutoProxy @ImportResource("classpath:/retryadvice.xml") public class AppConfig { ... }
同样,可以采用xml配置的方式完成:
监听者(Listeners)
注:笔者未验证这一章节
Listeners在重试时提供额外的回调。它们可以用于不同重试之间的各种横切关注点。
1、添加回调(Callbacks)
回调函数在RetryListener接口中提供:
public class DefaultListenerSupport extends RetryListenerSupport { @Override public
void close(RetryContext context, RetryCallback
callback, Throwable throwable) { logger.info("onClose"); ... super.close(context, callback, throwable); } @Override public
void onError(RetryContext context, RetryCallback
callback, Throwable throwable) { logger.info("onError"); ... super.onError(context, callback, throwable); } @Override public
boolean open(RetryContext context, RetryCallback
callback) { logger.info("onOpen"); ... return super.open(context, callback); } }
打开和关闭回调出现在整个重试之前和之后,onError应用于各个RetryCallback调用。
2、注册Listener
接着,将Listiener(DefaultListenerSupport)注册到RetryTemplate bean:
@Configuration public class AppConfig { ... @Bean public RetryTemplate retryTemplate() { RetryTemplate retryTemplate = new RetryTemplate(); ... retryTemplate.registerListener(new DefaultListenerSupport()); return retryTemplate; } }
3、测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration( classes = AppConfig.class, loader = AnnotationConfigContextLoader.class) public class SpringRetryTest { @Autowired private MyService myService; @Autowired private RetryTemplate retryTemplate; @Test(expected = RuntimeException.class) public void givenTemplateRetryService_whenCallWithException_thenRetry() { retryTemplate.execute(arg0 -> { myService.templateRetryService(); return null; }); } }
当运行测试用例时,下面的日志文本表示我们已经成功配置了RetryTemplate和Listener:
2017-01-09 20:04:10 [main] INFO o.b.s.DefaultListenerSupport - onOpen 2017-01-09 20:04:10 [main] INFO o.baeldung.springretry.MyServiceImpl - throw RuntimeException in method templateRetryService() 2017-01-09 20:04:10 [main] INFO o.b.s.DefaultListenerSupport - onError 2017-01-09 20:04:12 [main] INFO o.baeldung.springretry.MyServiceImpl - throw RuntimeException in method templateRetryService() 2017-01-09 20:04:12 [main] INFO o.b.s.DefaultListenerSupport - onError 2017-01-09 20:04:12 [main] INFO o.b.s.DefaultListenerSupport - onClose
注意事项(转载参考资料4)
1、使用了@Retryable的方法不能在本类被调用,不然重试机制不会生效。也就是要标记为@Service,然后在其它类使用@Autowired注入或者@Bean去实例才能生效。
2 、要触发@Recover方法,那么在@Retryable方法上不能有返回值,只能是void才能生效。
3 、非幂等情况下慎用
4 、使用了@Retryable的方法里面不能使用try…catch包裹,要在方法上抛出异常,不然不会触发
参考资料
1、https://www.baeldung.com/spring-retry(原文链接)
https://github.com/eugenp/tutorials/tree/master/spring-all(源代码链接)
2、https://www.jianshu.com/p/cc7abf
3、https://www.jianshu.com/p/58e753ca0151
4、https://my.oschina.net/wangjunBlog/blog/
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/215663.html原文链接:https://javaforall.net
