Spring retry(一)-使用指南

这篇具有很好参考价值的文章主要介绍了Spring retry(一)-使用指南。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、接入

spring boot 2.7.14

spring retry 从2.0.2版本之后,从spring batch里剥离出来成为一个单独的工程,因此我们引入spring retry最新版本可以直接如下引入

<dependency>
   <groupId>org.springframework.retry</groupId>
   <artifactId>spring-retry</artifactId>
   <version>2.0.2</version>
</dependency>

<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.7</version>
</dependency>

启动类上打上注解@EnableRetry
Spring retry(一)-使用指南,java

二、使用注解

Spring retry作为重试组件,可以直接使用@Retryable注解;废话不多说,直接上代码

@Component
public class UserService {


    @Retryable(retryFor = BizException.class, maxAttempts = 5, backoff = @Backoff(delay = 1000L, multiplier=1))
    public int service(int throwErr) throws BizException {

        System.out.println("===== service =====" + DateUtil.getDateTime(new Date()));

        if (throwErr==1) {
            throw new BizException();
        }

        return 1;
    }
}

执行效果如下

Spring retry(一)-使用指南,java

retryFor指定异常进行重试,如果不指定的话,默认任何异常都会重试;
maxAttempts重试次数,默认值是3次;
backoff是用于控制延迟重试策略,@Backoff(delay = 1000L, multiplier=2)表示每次执行失败,再次延迟时间=上次延迟时间*2

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Retryable {
    //最终失败情况下,调用recover进行恢复
    String recover() default "";

    //拦截器,主要是aop切面逻辑,具体待验证, 目前看下来主要是配合stateful一起使用,带验证
    String interceptor() default "";


    //包含哪些异常
    @AliasFor("include")
    Class<? extends Throwable>[] retryFor() default {};

    //哪些异常不重试
    @AliasFor("exclude")
    Class<? extends Throwable>[] noRetryFor() default {};

    //哪些异常不作回滚
    Class<? extends Throwable>[] notRecoverable() default {};

    //标签,没啥意义
    String label() default "";
   
    //有状态的重试,这个我们单独开讲
    boolean stateful() default false;

    //最大重试次数
    int maxAttempts() default 3;
    
    //最大重试次数表达式
    String maxAttemptsExpression() default "";

    //延迟策略
    Backoff backoff() default @Backoff;

    //异常过滤
    String exceptionExpression() default "";
    
    //监听器
    String[] listeners() default {};
}

三、使用RetryTemplate

上面通过注解@Retryable(retryFor = BizException.class, maxAttempts = 5, backoff = @Backoff(delay = 1000L, multiplier=1)) 的效果,可以通过如下编码的形式来实现

public int service2(int throwErr) throws BizException {
    RetryTemplate template = RetryTemplate.builder()
            .maxAttempts(3)
            .retryOn(BizException.class)
            .customBackoff(
                    BackOffPolicyBuilder
                            .newBuilder()
                            .delay(1000)
                            .multiplier(2)
                            .build()

            )
            .build();

    return template.execute(ctx->{
        System.out.println("===== service =====" + DateUtil.getDateTime(new Date()));

        if (throwErr==1) {
            throw new BizException("system error");
        }

        return 1;
    });
}

常见重试策略:

策略类 效果 关键参数
MaxAttemptsRetryPolicy 设置最大的重试次数,超过之后执行recover maxAttempts:最大重试次数
BinaryExceptionClassifierRetryPolicy 可以指定哪些异常需要重试,哪些异常不需要重试 exceptionClassifier:BinaryExceptionClassifier,异常识别类,其实就是存在一个Map映射
SimpleRetryPolicy 上面两者的结合,支持次数和自定义异常重试 maxAttempts 和 exceptionClassifier
TimeoutRetryPolicy 在指定的一段时间内重试 timeout:单位ms
ExceptionClassifierRetryPolicy 支持异常和重试策略的映射,比如异常A对应重试策略A,异常B对应重试策略B exceptionClassifier:本质就是异常和重试策略的映射
CompositeRetryPolicy 策略类的组合类,支持两种模式,乐观模式:只要一个重试策略满足即执行,悲观模式:另一种是所有策略模式满足再执行 policies:策略数组optimistic:true-乐观;false-悲观
CircuitBreakerRetryPolicy 熔断器模式 delegate:策略代理类openTimeout:[0,openTimeout]会一直执行代理类策略,(openTimeout, resetTimeout] 会半打开,如果代理类判断不可以重试,就会熔断,执行recover逻辑,如果代理类判断还可以重试且重试成功,开关会闭合。
resetTimeout:超过指定时间,开关重置闭合
ExpressionRetryPolicy 符合表达式就重试 expression:表达式,见 org.springframework.expression.Expression实现

常见回退策略:

策略类 效果 关键参数
FixedBackOffPolicy 间隔固定时间重试 sleeper:支持线程sleep和Object#wait,区别在于是否释放锁backOffPeriod:等待间隔
NoBackOffPolicy 无等待直接重试
ExponentialBackOffPolicy 在一个设置的时间区间内,等待时长为上一次时长的递增 initialInterval:默认起始等待时间
maxInterval:最大等待时间
multiplier:递增倍数
sleeper:同上
ExponentialRandomBackOffPolicy 在 ExponentialBackOffPolicy 基础上,乘数随机

四、关于RetryContentxt

普通场景下,重试是不需要获取之前重试的状态的,但是某些场景下,每次重试可能都需要打印当前重试次数,并且塞进去相关信息等

template.execute(ctx->{
    System.out.println("===== service =====" + DateUtil.getDateTime(new Date()));
    //上下文获取当前重试次数
    System.out.println("retry count="+ctx.getRetryCount());
    //也可以塞一些东西进去
    System.out.println("retry count="+ctx.getAttribute("attr"))
    //从属性里取值
    ctx.setAttribute("attr", "test"+ctx.getRetryCount());
    
    //直接终止当前重试,这个比较牛逼,配合固定重试次数来搞
    ctx.setExhaustedOnly()

    if (throwErr==1) {
        throw new BizException("system error");
    }

    return 1;
});

五、重试策略&延迟重试

我们直接把重试策略

public interface RetryPolicy extends Serializable {

   /**
    * @param context the current retry status
    * @return true if the operation can proceed
    */
   boolean canRetry(RetryContext context);

   /**
    * Acquire resources needed for the retry operation. The callback is passed in so that
    * marker interfaces can be used and a manager can collaborate with the callback to
    * set up some state in the status token.
    * @param parent the parent context if we are in a nested retry.
    * @return a {@link RetryContext} object specific to this policy.
    *
    */
   RetryContext open(RetryContext parent);

   /**
    * @param context a retry status created by the {@link #open(RetryContext)} method of
    * this policy.
    */
   void close(RetryContext context);

   /**
    * Called once per retry attempt, after the callback fails.
    * @param context the current status object.
    * @param throwable the exception to throw
    */
   void registerThrowable(RetryContext context, Throwable throwable);

}

Spring retry(一)-使用指南,java
从上图我们可以看到很多重试策略的实现,

六、recover

retry组件重试最终失败后,会调用recover方法(有点像回滚)

@Component
public class TestService {

    @Retryable(retryFor = RemoteAccessException.class)
    public void service() {
        System.out.println("===== service =====" + DateUtil.getDateTime(new Date()));
        throw new RemoteAccessException("xx");
    }
    @Recover
    public void recover(RemoteAccessException e) {
        System.out.println("===== recover =====" + DateUtil.getDateTime(new Date()));

    }

}

Spring retry(一)-使用指南,java

七、监听器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);

}

实现如下:

@Bean("listener1")
public RetryListener getListener() {
    return new RetryListener() {
        @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 onSuccess(RetryContext context, RetryCallback<T, E> callback, T result) {
            System.out.println("===== close =====");
        }
    };
}

然后在注解上引用

@Retryable(retryFor = RemoteAccessException.class, maxAttempts = 4,
        backoff = @Backoff(delay = 1000L, multiplier=2),
        listeners = {"listener1"}
)
public void service() {
    System.out.println("===== service =====" + DateUtil.getDateTime(new Date()));
    throw new RemoteAccessException("xx");
}

最终执行效果如下

Spring retry(一)-使用指南,java

八、有状态的重试stateful

有状态重试通常是用在message-driven 的应用中,从消息中间件比如RabbitMQ等接收到的消息,如果应用处理失败,那么消息中间件服务器会再次投递,再次投递时,对于集成了Spring Retry的应用来说,再次处理之前处理失败的消息,就是一次重试;也就是说,Spring Retry能够识别出,当前正在处理的消息是否是之前处理失败过的消息;

如果是之前处理过的消息,Spring Retry就可以使用 back off policies 阻塞当前线程;Spring Retry同时追踪重试的次数,支持处理彻底失败后的recover,这也是使用有状态重试的理由;

有状态重试的另一个典型应用场景是跟Spring Transaction框架集成。在集成了Spring Transaction框架的MVC应用中,通过TransactionInterceptor,开启对Service层的事务管理;在这种情况下,Spring Retry会提供让每一次重试和重试次数耗尽之后的recover都在一个新的事务中执行。

九、retry组件的忧缺点

整个使用下来,retry组件的优点:

  1. 无侵入式的实现了重试,大大减小了重试代码成本
  2. 重试策略比较灵活,支持固定频率重试、延迟重试等策略

缺点:文章来源地址https://www.toymoban.com/news/detail-686057.html

  1. 不支持异步重试,且重试过程是阻塞当前程序的,当然,如果要实现异步重试,需要配合@Async注解来搞

到了这里,关于Spring retry(一)-使用指南的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Cloud OpenFeign 的使用及踩坑指南

    Feign 和OpenFeign Feign OpenFeign openFeign的优势 OpenFeign应用 1. 导入依赖 2. 使用 3. 日志配置 4. 数据压缩 OpenFeign高级应用 OpenFeign熔断降级的两种方式-降级方法和降级工厂 踩坑指南 坑一:Http Client 坑二:全局超时时间 坑三:单服务设置超时时间 遇到的问题 1. 使用Spring MVC注解,但请

    2024年02月14日
    浏览(50)
  • jasypt-spring-boot敏感信息加密解密利器使用指南

    Springboot 整合Jasypt,实现配置信息的安全,如数据库连接.账号和密码.接口凭证信息等。 Jasypt可以为Springboot加密的信息很多,主要有: System Property 系统变量 Envirnment Property 环境变量 Command Line argument 命令行参数 Application.properties 应用配置文件 Yaml properties 应用配置文件 other

    2024年02月03日
    浏览(52)
  • Java 21 虚拟线程:使用指南(一)

    虚拟线程是由 Java 21 版本中实现的一种轻量级线程。它由 JVM 进行创建以及管理。虚拟线程和传统线程(我们称之为平台线程)之间的主要区别在于,我们可以轻松地在一个 Java 程序中运行大量、甚至数百万个虚拟线程。 由于虚拟线程的数量众多,也就赋予了 Java 程序强大的

    2024年02月04日
    浏览(45)
  • 2. 使用IDEA创建Spring Boot Hello项目并管理依赖——Maven入门指南

    前言:本文将介绍如何使用IDEA创建一个Spring Boot Hello项目,并通过Maven来管理项目的依赖。我们从项目的创建到代码的编写,再到项目的构建和运行,一步步演示了整个过程。 🚀 作者简介:作为某云服务提供商的后端开发人员,我将在这里与大家简要分享一些实用的开发小

    2024年02月10日
    浏览(59)
  • Java 新技术:虚拟线程使用指南(二)

    虚拟线程是在 Java 21 版本中实现的一种轻量级线程。它由 JVM 进行创建以及管理。虚拟线程和传统线程(我们称之为平台线程)之间的主要区别在于,我们可以轻松地在一个 Java 程序中运行大量、甚至数百万个虚拟线程。 由于虚拟线程的数量众多,也就赋予了 Java 程序强大的

    2024年02月03日
    浏览(40)
  • 使用Java实现的OpenCV开发指南

    使用Java实现的OpenCV开发指南 OpenCV是一个优秀的计算机视觉库,包含了数百个算法和函数,可用于处理图像和视频。而Java语言不仅具备面向对象编程的优点,同时还在互联网开发、移动应用、企业级应用等方面广泛应用。那么如何结合二者,使用Java实现基于OpenCV的图像处理呢

    2024年02月06日
    浏览(50)
  • 【IDEA使用指南】使用Hibernate框架的Java项目,如何通过数据库表自动生成实体模型?

    步骤1:找到并打开“Persistence”工具栏。 如下图所示,找到 “View - Tool Windows - Persistence”,点击“Persistence”。 步骤2:找到并打开“Import Database Schema” 窗口。 在开发工具左下角会弹出持久化配置的工具栏“Persistence”,如下图所示。单击之后有一个弹框,找到弹框中的项

    2024年02月05日
    浏览(65)
  • 快速去重:使用Java根据对象某一属性去除重复对象的实现指南

    🧐📚 Java中的对象去重操作?跟着小编一起学习吧!👇 在处理对象集合时,有时候我们需要根据对象的某个属性进行去重操作。Java给我们提供了多种方法来实现这个功能。今天,小编就来给大家介绍一下如何使用Java根据对象的某个属性进行去重操作。💫 提供一个自定义的

    2024年02月04日
    浏览(65)
  • 【实践篇】Redis最强Java客户端(四)之Ression分布式集合使用指南

    前两章我们了解了《【实践篇】Redis最强Java客户端(一)之Redisson入门介绍》和《【实践篇】Redis最强Java客户端(二)之Redisson基础概念》 本章第四章主要介绍Ression分布式集合使用指南。 上一章《Redisson 7种分布式锁使用指南》回顾。 本章我们介绍了在Redisson中实现的各种分布式集

    2024年02月09日
    浏览(51)
  • 深入了解Kubernetes(k8s):安装、使用和Java部署指南(持续更新中)

    Docker和Kubernetes是两个不同的概念和技术,它们在容器化应用和容器编排方面有着不同的功能和作用。 Docker: Docker是一个开源的容器化平台,用于构建、打包和运行应用程序。通过使用Docker,你可以将应用程序及其依赖项打包到一个独立的轻量级容器中,使其可以在不同的环

    2024年02月10日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包