Spring Retry 重试实例详解

作者:废物大师兄 时间:2021-07-15 22:43:26 

spring-retry是什么?

spring-retry是spring提供的一个重试框架,原本自己实现的重试机制,现在spring帮封装好提供更加好的编码体验。

重试的使用场景比较多,比如调用远程服务时,由于网络或者服务端响应慢导致调用超时,此时可以多重试几次。用定时任务也可以实现重试的效果,但比较麻烦,用Spring Retry的话一个注解搞定所有。话不多说,先看演示。

首先引入依赖

<dependency>
   <groupId>org.springframework.retry</groupId>
   <artifactId>spring-retry</artifactId>
   <version>1.3.4</version>
</dependency>
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.9.1</version>
</dependency>

使用方式有两种:命令式和声明式

命令式

/**
* 命令式的方式使用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<String, Throwable>() {
       @Override
       public String doWithRetry(RetryContext context) throws Throwable {
           return helloService.hello(code);
       }
   });

return resp;
}

定义一个RetryTemplate,然后调用execute方法,可配置项比较多,不一一列举

真正使用的时候RetryTemplate可以定义成一个Bean,例如:

@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;
   }
}

业务代码:

/**
* 命令式的方式使用Spring Retry
*/
@Override
public String hello(int code) {
   if (0 == code) {
       System.out.println("出错了");
       throw new RemoteAccessException("出错了");
   }
   System.out.println("处理完成");
   return "ok";
}

Spring Retry 重试实例详解

Spring Retry 重试实例详解

Spring Retry 重试实例详解

* 实现:

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 <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
       System.out.println("open");
       return true;
   }

@Override
   public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
       System.out.println("close");
   }

@Override
   public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
       System.out.println("error");
   }
}

声明式(注解方式)

/**
* 注解的方式使用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修饰的方法一样,除了第一个参数外

/**
* 注解的方式使用Spring Retry
*/
@GetMapping("/hi")
public String hi(@RequestParam("code") Integer code) {
   return helloService.hi(code);
}

Spring Retry 重试实例详解

Spring Retry 重试实例详解

Spring Retry 重试实例详解

Spring Retry 重试实例详解

1. 用法

声明式

@Configuration
@EnableRetry
public class Application {

}
@Service
class Service {
   @Retryable(RemoteAccessException.class)
   public void service() {
       // ... do something
   }
   @Recover
   public void recover(RemoteAccessException e) {
      // ... panic
   }
}

命令式

RetryTemplate template = RetryTemplate.builder()
.maxAttempts(3)
.fixedBackoff(1000)
.retryOn(RemoteAccessException.class)
.build();

template.execute(ctx -> {
   // ... do something
});

2. RetryTemplate

为了自动重试,Spring Retry 提供了 RetryOperations 重试操作策略

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;

}

基本回调是一个简单的接口,允许插入一些要重试的业务逻辑:

public interface RetryCallback<T> {

T doWithRetry(RetryContext context) throws Throwable;
}

回调函数被尝试,如果失败(通过抛出异常),它将被重试,直到成功或实现决定中止。

RetryOperations最简单的通用实现是RetryTemplate

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 operation
       return result;
   }

});

从Spring Retry 1.3开始,RetryTemplate支持流式配置:

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。

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
   }
});

4. Listeners

public interface RetryListener {

void open(RetryContext context, RetryCallback<T> callback);

void onSuccess(RetryContext context, T result);

void onError(RetryContext context, RetryCallback<T> callback, Throwable e);

void close(RetryContext context, RetryCallback<T> callback, Throwable e);
}

在最简单的情况下,open和close回调在整个重试之前和之后,onSuccess和onError应用于个别的RetryCallback调用,onSuccess方法在成功调用回调之后被调用。

5. 声明式重试

有时,你希望在每次业务处理发生时都重试一些业务处理。这方面的典型例子是远程服务调用。Spring Retry提供了一个AOP * ,它将方法调用封装在RetryOperations实例中。RetryOperationsInterceptor执行被拦截的方法,并根据提供的RepeatTemplate中的RetryPolicy在失败时重试。

你可以在 @Configuration 类上添加一个 @EnableRetry 注解,并且在你想要进行重试的方法(或者类)上添加 @Retryable 注解,还可以指定任意数量的重试 * 。

@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

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

如果希望在重试用尽时采用替代代码返回,则可以提供恢复方法。方法应该声明在与@Retryable实例相同的类中,并标记为@Recover。返回类型必须匹配@Retryable方法。恢复方法的参数可以包括抛出的异常和(可选地)传递给原始可重试方法的参数(或者它们的部分列表,只要在需要的最后一个之前不省略任何参数)。

@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
   }
}

若要解决可选择用于恢复的多个方法之间的冲突,可以显式指定恢复方法名称。

@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

来源:https://www.cnblogs.com/cjsblog/p/16826063.html

标签:Spring,Retry,重试
0
投稿

猜你喜欢

  • 微信第三方登录Android实现代码

    2023-07-27 08:05:49
  • C# 7.2中结构体性能问题的解决方案

    2022-08-12 23:04:26
  • 使用 Spring Boot 2.0 + WebFlux 实现 RESTful API功能

    2023-12-22 19:51:03
  • Java结构型模式之门面模式详解

    2023-07-29 10:26:55
  • Android基于HttpUrlConnection类的文件下载实例代码

    2022-08-28 02:18:49
  • SpringBoot集成Mybatis-plus并实现自动生成相关文件的示例代码

    2023-08-15 04:12:46
  • C#数据结构与算法揭秘五 栈和队列

    2023-09-13 13:31:47
  • 关于IDEA中spring-cloud-starter-alibaba-nacos-discovery 无法引入问题

    2022-09-14 19:34:21
  • Nacos入门过程的坑--获取不到配置的值问题

    2023-07-24 11:39:41
  • 关于MyBatis plus条件构造器的逐条详解

    2021-10-06 07:31:51
  • Android音视频开发只硬件解码组件MediaCodec讲解

    2023-08-20 10:33:20
  • 深入XPath的详解以及Java示例代码分析

    2021-11-01 13:42:33
  • RocketMQ实现随缘分BUG小功能示例详解

    2023-01-20 04:21:05
  • Java循环结构之多重循环及continue break

    2023-11-10 15:39:14
  • Unity shader实现百叶窗特效

    2023-06-05 00:05:49
  • Android中的常用尺寸单位(dp、sp)快速入门教程

    2023-09-19 08:59:08
  • sweet alert dialog 在android studio应用问题说明详解

    2022-12-14 04:17:53
  • C# TabControl控件中TabPage选项卡切换时的触发事件问题

    2022-10-30 08:11:13
  • unity使用射线实现贴花系统

    2023-04-23 10:22:00
  • c#如何实现接口事件

    2023-10-02 18:31:28
  • asp之家 软件编程 m.aspxhome.com