[JAVA] Spring Retry 重试实例详解

1929 0
王子 2022-11-8 17:59:12 | 显示全部楼层 |阅读模式
spring-retry是什么?

spring-retry是spring提供的一个重试框架,原本自己实现的重试机制,现在spring帮封装好提供更加好的编码体验。
重试的使用场景比较多,比如调用远程服务时,由于网络或者服务端响应慢导致调用超时,此时可以多重试几次。用定时任务也可以实现重试的效果,但比较麻烦,用Spring Retry的话一个注解搞定所有。话不多说,先看演示。
首先引入依赖
  1. <dependency>
  2.     <groupId>org.springframework.retry</groupId>
  3.     <artifactId>spring-retry</artifactId>
  4.     <version>1.3.4</version>
  5. </dependency>
  6. <dependency>
  7.     <groupId>org.aspectj</groupId>
  8.     <artifactId>aspectjweaver</artifactId>
  9.     <version>1.9.9.1</version>
  10. </dependency>
复制代码
使用方式有两种:命令式和声明式
命令式
  1. /**
  2. * 命令式的方式使用Spring Retry
  3. */
  4. @GetMapping("/hello")
  5. public String hello(@RequestParam("code") Integer code) throws Throwable {
  6.     RetryTemplate retryTemplate = RetryTemplate.builder()
  7.             .maxAttempts(3)
  8.             .fixedBackoff(1000)
  9.             .retryOn(RemoteAccessException.class)
  10.             .build();
  11.     retryTemplate.registerListener(new MyRetryListener());
  12.     String resp = retryTemplate.execute(new RetryCallback<String, Throwable>() {
  13.         @Override
  14.         public String doWithRetry(RetryContext context) throws Throwable {
  15.             return helloService.hello(code);
  16.         }
  17.     });
  18.     return resp;
  19. }
复制代码
定义一个RetryTemplate,然后调用execute方法,可配置项比较多,不一一列举
真正使用的时候RetryTemplate可以定义成一个Bean,例如:
  1. @Configuration
  2. public class RetryConfig {
  3.     @Bean
  4.     public RetryTemplate retryTemplate() {
  5.         RetryTemplate retryTemplate = RetryTemplate.builder()
  6.                 .maxAttempts(3)
  7.                 .fixedBackoff(1000)
  8.                 .withListener(new MyRetryListener())
  9.                 .retryOn(RemoteAccessException.class)
  10.                 .build();
  11.         return retryTemplate;
  12.     }
  13. }
复制代码
业务代码:
  1. /**
  2. * 命令式的方式使用Spring Retry
  3. */
  4. @Override
  5. public String hello(int code) {
  6.     if (0 == code) {
  7.         System.out.println("出错了");
  8.         throw new RemoteAccessException("出错了");
  9.     }
  10.     System.out.println("处理完成");
  11.     return "ok";
  12. }
复制代码






监听器实现:
  1. package com.example.retry.listener;
  2. import org.springframework.retry.RetryCallback;
  3. import org.springframework.retry.RetryContext;
  4. import org.springframework.retry.RetryListener;
  5. public class MyRetryListener implements RetryListener {
  6.     @Override
  7.     public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
  8.         System.out.println("open");
  9.         return true;
  10.     }
  11.     @Override
  12.     public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
  13.         System.out.println("close");
  14.     }
  15.     @Override
  16.     public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
  17.         System.out.println("error");
  18.     }
  19. }
复制代码
声明式(注解方式)
  1. /**
  2. * 注解的方式使用Spring Retry
  3. */
  4. @Retryable(value = Exception.class, maxAttempts = 2, backoff = @Backoff(value = 1000, delay = 2000, multiplier = 0.5))
  5. @Override
  6. public String hi(int code) {
  7.     System.out.println("方法被调用");
  8.     int a = 1/code;
  9.     return "ok";
  10. }
  11. @Recover
  12. public String hiRecover(Exception ex, int code) {
  13.     System.out.println("重试结束");
  14.     return "asdf";
  15. }
复制代码
这里需要主要的几点
    @EnableRetry(proxyTargetClass = true/false)@Retryable 修饰的方法必须是public的,而且不能是同一个类中调用@Recover 修饰的方法签名必须与@Retryable修饰的方法一样,除了第一个参数外
  1. /**
  2. * 注解的方式使用Spring Retry
  3. */
  4. @GetMapping("/hi")
  5. public String hi(@RequestParam("code") Integer code) {
  6.     return helloService.hi(code);
  7. }
复制代码








1. 用法

声明式
  1. @Configuration
  2. @EnableRetry
  3. public class Application {
  4. }
  5. @Service
  6. class Service {
  7.     @Retryable(RemoteAccessException.class)
  8.     public void service() {
  9.         // ... do something
  10.     }
  11.     @Recover
  12.     public void recover(RemoteAccessException e) {
  13.        // ... panic
  14.     }
  15. }
复制代码
命令式
  1. RetryTemplate template = RetryTemplate.builder()
  2.                                 .maxAttempts(3)
  3.                                 .fixedBackoff(1000)
  4.                                 .retryOn(RemoteAccessException.class)
  5.                                 .build();
  6. template.execute(ctx -> {
  7.     // ... do something
  8. });
复制代码
2. RetryTemplate

为了自动重试,Spring Retry 提供了 RetryOperations 重试操作策略
  1. public interface RetryOperations {
  2.     <T> T execute(RetryCallback<T> retryCallback) throws Exception;
  3.     <T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback)
  4.         throws Exception;
  5.     <T> T execute(RetryCallback<T> retryCallback, RetryState retryState)
  6.         throws Exception, ExhaustedRetryException;
  7.     <T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback,
  8.         RetryState retryState) throws Exception;
  9. }
复制代码
基本回调是一个简单的接口,允许插入一些要重试的业务逻辑:
  1. public interface RetryCallback<T> {
  2.     T doWithRetry(RetryContext context) throws Throwable;
  3. }
复制代码
回调函数被尝试,如果失败(通过抛出异常),它将被重试,直到成功或实现决定中止。
RetryOperations最简单的通用实现是RetryTemplate
  1. RetryTemplate template = new RetryTemplate();
  2. TimeoutRetryPolicy policy = new TimeoutRetryPolicy();
  3. policy.setTimeout(30000L);
  4. template.setRetryPolicy(policy);
  5. Foo result = template.execute(new RetryCallback<Foo>() {
  6.     public Foo doWithRetry(RetryContext context) {
  7.         // Do stuff that might fail, e.g. webservice operation
  8.         return result;
  9.     }
  10. });
复制代码
从Spring Retry 1.3开始,RetryTemplate支持流式配置:
  1. RetryTemplate.builder()
  2.       .maxAttempts(10)
  3.       .exponentialBackoff(100, 2, 10000)
  4.       .retryOn(IOException.class)
  5.       .traversingCauses()
  6.       .build();
  7. RetryTemplate.builder()
  8.       .fixedBackoff(10)
  9.       .withinMillis(3000)
  10.       .build();
  11. RetryTemplate.builder()
  12.       .infiniteRetry()
  13.       .retryOn(IOException.class)
  14.       .uniformRandomBackoff(1000, 3000)
  15.       .build();
复制代码
3. RecoveryCallback

当重试耗尽时,RetryOperations可以将控制传递给不同的回调:RecoveryCallback。
  1. Foo foo = template.execute(new RetryCallback<Foo>() {
  2.     public Foo doWithRetry(RetryContext context) {
  3.         // business logic here
  4.     },
  5.   new RecoveryCallback<Foo>() {
  6.     Foo recover(RetryContext context) throws Exception {
  7.           // recover logic here
  8.     }
  9. });
复制代码
4. Listeners
  1. public interface RetryListener {
  2.     void open(RetryContext context, RetryCallback<T> callback);
  3.     void onSuccess(RetryContext context, T result);
  4.     void onError(RetryContext context, RetryCallback<T> callback, Throwable e);
  5.     void close(RetryContext context, RetryCallback<T> callback, Throwable e);
  6. }
复制代码
在最简单的情况下,open和close回调在整个重试之前和之后,onSuccess和onError应用于个别的RetryCallback调用,onSuccess方法在成功调用回调之后被调用。
5. 声明式重试

有时,你希望在每次业务处理发生时都重试一些业务处理。这方面的典型例子是远程服务调用。Spring Retry提供了一个AOP拦截器,它将方法调用封装在RetryOperations实例中。RetryOperationsInterceptor执行被拦截的方法,并根据提供的RepeatTemplate中的RetryPolicy在失败时重试。
你可以在 @Configuration 类上添加一个 @EnableRetry 注解,并且在你想要进行重试的方法(或者类)上添加 @Retryable 注解,还可以指定任意数量的重试监听器。
  1. @Configuration
  2. @EnableRetry
  3. public class Application {
  4.     @Bean
  5.     public Service service() {
  6.         return new Service();
  7.     }
  8.     @Bean public RetryListener retryListener1() {
  9.         return new RetryListener() {...}
  10.     }
  11.     @Bean public RetryListener retryListener2() {
  12.         return new RetryListener() {...}
  13.     }
  14. }
  15. @Service
  16. class Service {
  17.     @Retryable(RemoteAccessException.class)
  18.     public service() {
  19.         // ... do something
  20.     }
  21. }
复制代码
可以使用 @Retryable 的属性类控制 RetryPolicy 和 BackoffPolicy
  1. @Service
  2. class Service {
  3.     @Retryable(maxAttempts=12, backoff=@Backoff(delay=100, maxDelay=500))
  4.     public service() {
  5.         // ... do something
  6.     }
  7. }
复制代码
如果希望在重试用尽时采用替代代码返回,则可以提供恢复方法。方法应该声明在与@Retryable实例相同的类中,并标记为@Recover。返回类型必须匹配@Retryable方法。恢复方法的参数可以包括抛出的异常和(可选地)传递给原始可重试方法的参数(或者它们的部分列表,只要在需要的最后一个之前不省略任何参数)。
  1. @Service
  2. class Service {
  3.     @Retryable(RemoteAccessException.class)
  4.     public void service(String str1, String str2) {
  5.         // ... do something
  6.     }
  7.     @Recover
  8.     public void recover(RemoteAccessException e, String str1, String str2) {
  9.        // ... error handling making use of original args if required
  10.     }
  11. }
复制代码
若要解决可选择用于恢复的多个方法之间的冲突,可以显式指定恢复方法名称。
  1. @Service
  2. class Service {
  3.     @Retryable(recover = "service1Recover", value = RemoteAccessException.class)
  4.     public void service1(String str1, String str2) {
  5.         // ... do something
  6.     }
  7.     @Retryable(recover = "service2Recover", value = RemoteAccessException.class)
  8.     public void service2(String str1, String str2) {
  9.         // ... do something
  10.     }
  11.     @Recover
  12.     public void service1Recover(RemoteAccessException e, String str1, String str2) {
  13.         // ... error handling making use of original args if required
  14.     }
  15.     @Recover
  16.     public void service2Recover(RemoteAccessException e, String str1, String str2) {
  17.         // ... error handling making use of original args if required
  18.     }
  19. }
复制代码
https://github.com/spring-projects/spring-retry
到此这篇关于Spring Retry 重试的文章就介绍到这了,更多相关Spring Retry 重试内容请搜索中国红客联盟以前的文章或继续浏览下面的相关文章希望大家以后多多支持中国红客联盟!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行