重试的使用场景比较多,比如调用远程服务时,由于网络或者服务端响应慢导致调用超时,此时可以多重试几次。用定时任务也可以实现重试的效果,但比较麻烦,用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";
}

监听器实现:

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

这里需要主要的几点

  1. @EnableRetry(proxyTargetClass = true/false)
  2. @Retryable 修饰的方法必须是public的,而且不能是同一个类中调用
  3. @Recover 修饰的方法签名必须与@Retryable修饰的方法一样,除了第一个参数外
/**
* 注解的方式使用Spring Retry
*/
@GetMapping("/hi")
public String hi(@RequestParam("code") Integer code) {
return helloService.hi(code);
}

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

Spring Retry 重试的更多相关文章

  1. 自己动手实践 spring retry 重试框架

    前序 马上过年了,预祝大家,新年快乐,少写bug 什么是spring retry? spring retry是从spring batch独立出来的一个能功能,主要实现了重试和熔断. 什么时候用? 远程 ...

  2. 012 spring retry重试原理的解析

    有点复杂,在后续的章节,将会对其中涉及到的知识点,再分章节进行说明. 1.程序结构 2.@Retryable package com.jun.web.annotation.theory; import ...

  3. spring retry 重试机制完整例子

    public static Boolean vpmsRetryCoupon(final String userId) { // 构建重试模板实例 RetryTemplate retryTemplate ...

  4. Spring异常重试框架Spring Retry

    Spring Retry支持集成到Spring或者Spring Boot项目中,而它支持AOP的切面注入写法,所以在引入时必须引入aspectjweaver.jar包. 快速集成的代码样例: @Con ...

  5. 异常重试框架Spring Retry实践

    前期准备在Maven项目中添加Spring Retry和切面的依赖 POM: <!-- Spring Retry --> <dependency> <groupId> ...

  6. Spring retry基本使用

    Spring retry基本使用 背景介绍 在实际工作过程中,重试是一个经常使用的手段.比如MQ发送消息失败,会采取重试手段,比如工程中使用RPC请求外部服务,可能因为网络 波动出现超时而采取重试手段 ...

  7. Spring Retry

    最近组内准备将项目中原有的重试功能抽取出来重构为一个重试平台,由于对重试的功能要求比较高,采用了不少中间件和框架(jimdb,jproxy, Elastic-Job ,JMQ,Hbase, Disru ...

  8. Spring retry实践

    在开发中,重试是一个经常使用的手段.比如MQ发送消息失败,会采取重试手段,比如工程中使用RPC请求外部服务,可能因为网络波动出现超时而采取重试手段......可以看见重试操作是非常常见的一种处理问题, ...

  9. 【spring】spring retry介绍

    一.为什么需要重试? 我们知道只要是网络请求都有失败的情况,这个时候增加retry机制是必要的.而spring全家桶中就有这么一套机制. 二.spring retry spring系列的spring ...

随机推荐

  1. mybatis 15: 缓存

    作用 当对某些数据的查询请求频繁,且数据不经常修改时,使用缓存机制可以提高查询效率 注意 mybatis专注于sql查询,数据映射 缓存问题应该交给专门负责缓存的其他第三方框架 mybatis缓存执行 ...

  2. openjdk的bug

    容器内就获取个cpu利用率,怎么就占用单核100%了呢 背景:这个是在centos7 + lxcfs 和jdk11 的环境上复现的 下面列一下我们是怎么排查并解这个问题的. 一.故障现象 oppo内核 ...

  3. 如何在 HTML 中调整图像大小?

    了解在 HTML 中调整图像大小的不同技术.何时应避免在浏览器端调整大小,以及在 Web 上操作和提供图像的正确方法. 如果您的图像不适合布局,您可以在 HTML 中调整其大小.在 HTML 中调整图 ...

  4. 【JAVA】学习路径35-InputStream使用例子

    import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; pu ...

  5. 六 抽象工厂模式【Abstract Factory Pattern】 来自CBF4LIFE 的设计模式0

    好了,我们继续上一节课,上一节讲到女娲造人,人是造出来了,世界时热闹了,可是低头一看,都是清一色的类型,缺少关爱.仇恨.喜怒哀乐等情绪,人类的生命太平淡了,女娲一想,猛然一拍脑袋,Shit!忘记给人类 ...

  6. UIView Animation 动画学习总结

    目录 一.前言 二.UIView Animation 2.1 简单动画 2.2 关键帧动画 2.3 View 的转换 三.CALayer Animation 3.1 基本动画(CABasicAnima ...

  7. 解决CDH 访问权限问题

    CDH 6.2 安装好以后,直接使用root 或者 其他账号执行spark-shell 会报权限错误 22/01/04 17:46:28 ERROR spark.SparkContext: Error ...

  8. 用【Unity】中的【3D Object】画【数学函数】图形 —— 正弦函数... { }

    效果 场景搭建 创建一个空物体,并将其命名为 "GameManager",并[Reset]它 创建一个 "Sphere",并将其[Scale]设置为(0.1,0 ...

  9. 注解@DependsOn解析

    作用 @DependsOn注解可以定义在类和方法上,意思是我这个组件要依赖于另一个组件,也就是说被依赖的组件会比该组件先注册到IOC容器中. 在哪里被解析 解析的地方在 ComponentScanAn ...

  10. Windows Server体验之管理

    安装了只有命令行界面的Windows Server之后怎么去管理,对于传统的Windows管理员来说确实是比较棘手的.因为没有了图形化的管理界面,需要更多的去依赖Powershell或者cmd命令去做 ...