spring-retry是什么?
spring-retry是spring提供的一个重试框架,原本自己实现的重试机制,现在spring帮封装好提供更加好的编码体验。
重试的使用场景比较多,比如调用远程服务时,由于网络或者服务端响应慢导致调用超时,此时可以多重试几次。用定时任务也可以实现重试的效果,但比较麻烦,用Spring Retry的话一个注解搞定所有。话不多说,先看演示。
首先引入依赖
1 | org.springframework.retryspring-retry1.3.4org.aspectjaspectjweaver1.9.9.1 |
使用方式有两种:命令式和声明式
命令式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /** * 命令式的方式使用Spring Retry */ @GetMapping ( "/hello" ) public String hello( @RequestParam ( "code" ) Integer code) throws Throwable { RetryTemplate retryTemplate = RetryTemplate.builder() .maxAttempts( 3 ) .fixedBackoff( 1000 ) .retryOn(RemoteAccessException. class ) .build(); retryTemplate.registerListener( new MyRetryListener()); String resp = retryTemplate.execute( new RetryCallback() { @Override public String doWithRetry(RetryContext context) throws Throwable { return helloService.hello(code); } }); return resp; } |
定义一个RetryTemplate,然后调用execute方法,可配置项比较多,不一一列举
真正使用的时候RetryTemplate可以定义成一个Bean,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 | @Configuration public class RetryConfig { @Bean public RetryTemplate retryTemplate() { RetryTemplate retryTemplate = RetryTemplate.builder() .maxAttempts( 3 ) .fixedBackoff( 1000 ) .withListener( new MyRetryListener()) .retryOn(RemoteAccessException. class ) .build(); return retryTemplate; } } |
业务代码:
1 2 3 4 5 6 7 8 9 10 11 12 | /** * 命令式的方式使用Spring Retry */ @Override public String hello( int code) { if ( 0 == code) { System.out.println( "出错了" ); throw new RemoteAccessException( "出错了" ); } System.out.println( "处理完成" ); return "ok" ; } |
监听器实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package com.example.retry.listener; import org.springframework.retry.RetryCallback; import org.springframework.retry.RetryContext; import org.springframework.retry.RetryListener; public class MyRetryListener implements RetryListener { @Override public boolean open(RetryContext context, RetryCallback callback) { System.out.println( "open" ); return true ; } @Override public void close(RetryContext context, RetryCallback callback, Throwable throwable) { System.out.println( "close" ); } @Override public void onError(RetryContext context, RetryCallback callback, Throwable throwable) { System.out.println( "error" ); } } |
声明式(注解方式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /** * 注解的方式使用Spring Retry */ @Retryable (value = Exception. class , maxAttempts = 2 , backoff = @Backoff (value = 1000 , delay = 2000 , multiplier = 0.5 )) @Override public String hi( int code) { System.out.println( "方法被调用" ); int a = 1 /code; return "ok" ; } @Recover public String hiRecover(Exception ex, int code) { System.out.println( "重试结束" ); return "asdf" ; } |
这里需要主要的几点
- @EnableRetry(proxyTargetClass = true/false)
- @Retryable 修饰的方法必须是public的,而且不能是同一个类中调用
- @Recover 修饰的方法签名必须与@Retryable修饰的方法一样,除了第一个参数外
1 2 3 4 5 6 7 | /** * 注解的方式使用Spring Retry */ @GetMapping ( "/hi" ) public String hi( @RequestParam ( "code" ) Integer code) { return helloService.hi(code); } |
1. 用法
声明式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @Configuration @EnableRetry public class Application { } @Service class Service { @Retryable (RemoteAccessException. class ) public void service() { // ... do something } @Recover public void recover(RemoteAccessException e) { // ... panic } } |
命令式
1 2 3 4 5 6 7 8 9 | RetryTemplate template = RetryTemplate.builder() .maxAttempts( 3 ) .fixedBackoff( 1000 ) .retryOn(RemoteAccessException. class ) .build(); template.execute(ctx -> { // ... do something }); |
2. RetryTemplate
为了自动重试,Spring Retry 提供了 RetryOperations 重试操作策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public interface RetryOperations { T execute(RetryCallback retryCallback) throws Exception; T execute(RetryCallback retryCallback, RecoveryCallback recoveryCallback) throws Exception; T execute(RetryCallback retryCallback, RetryState retryState) throws Exception, ExhaustedRetryException; T execute(RetryCallback retryCallback, RecoveryCallback recoveryCallback, RetryState retryState) throws Exception; } |
基本回调是一个简单的接口,允许插入一些要重试的业务逻辑:
1 2 3 4 | public interface RetryCallback { T doWithRetry(RetryContext context) throws Throwable; } |
回调函数被尝试,如果失败(通过抛出异常),它将被重试,直到成功或实现决定中止。
RetryOperations最简单的通用实现是RetryTemplate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | RetryTemplate template = new RetryTemplate(); TimeoutRetryPolicy policy = new TimeoutRetryPolicy(); policy.setTimeout(30000L); template.setRetryPolicy(policy); Foo result = template.execute( new RetryCallback() { public Foo doWithRetry(RetryContext context) { // Do stuff that might fail, e.g. webservice operation return result; } }); |
从Spring Retry 1.3开始,RetryTemplate支持流式配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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(); |
3. RecoveryCallback
当重试耗尽时,RetryOperations可以将控制传递给不同的回调:RecoveryCallback。
1 2 3 4 5 6 7 8 9 | Foo foo = template.execute( new RetryCallback() { public Foo doWithRetry(RetryContext context) { // business logic here }, new RecoveryCallback() { Foo recover(RetryContext context) throws Exception { // recover logic here } }); |
4. Listeners
1 2 3 4 5 6 7 8 9 10 | public interface RetryListener { void open(RetryContext context, RetryCallback callback); void onSuccess(RetryContext context, T result); void onError(RetryContext context, RetryCallback callback, Throwable e); void close(RetryContext context, RetryCallback callback, Throwable e); } |
在最简单的情况下,open和close回调在整个重试之前和之后,onSuccess和onError应用于个别的RetryCallback调用,onSuccess方法在成功调用回调之后被调用。
5. 声明式重试
有时,你希望在每次业务处理发生时都重试一些业务处理。这方面的典型例子是远程服务调用。Spring Retry提供了一个AOP拦截器,它将方法调用封装在RetryOperations实例中。RetryOperationsInterceptor执行被拦截的方法,并根据提供的RepeatTemplate中的RetryPolicy在失败时重试。
你可以在 @Configuration 类上添加一个 @EnableRetry 注解,并且在你想要进行重试的方法(或者类)上添加 @Retryable 注解,还可以指定任意数量的重试监听器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | @Configuration @EnableRetry public class Application { @Bean public 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 } } |
可以使用 @Retryable 的属性类控制 RetryPolicy 和 BackoffPolicy
1 2 3 4 5 6 7 | @Service class Service { @Retryable (maxAttempts= 12 , backoff= @Backoff (delay= 100 , maxDelay= 500 )) public service() { // ... do something } } |
如果希望在重试用尽时采用替代代码返回,则可以提供恢复方法。方法应该声明在与@Retryable实例相同的类中,并标记为@Recover。返回类型必须匹配@Retryable方法。恢复方法的参数可以包括抛出的异常和(可选地)传递给原始可重试方法的参数(或者它们的部分列表,只要在需要的最后一个之前不省略任何参数)。
1 2 3 4 5 6 7 8 9 10 11 | @Service class Service { @Retryable (RemoteAccessException. class ) public void service(String str1, String str2) { // ... do something } @Recover public void recover(RemoteAccessException e, String str1, String str2) { // ... error handling making use of original args if required } } |
若要解决可选择用于恢复的多个方法之间的冲突,可以显式指定恢复方法名称。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @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 } } |
https://github.com/spring-projects/spring-retry
到此这篇关于Spring Retry 重试的文章就介绍到这了,更多相关Spring Retry 重试内容请搜索IT俱乐部以前的文章或继续浏览下面的相关文章希望大家以后多多支持IT俱乐部!