【Spring专题】「实战系列」重新回顾一下异常重试框架Spring Retry的功能指南

简介: 【Spring专题】「实战系列」重新回顾一下异常重试框架Spring Retry的功能指南

重试机制的业务背景


外部服务对于调用者来说一般都是不可靠的,尤其是在网络环境比较差的情况下,网络抖动很容易导致请求超时等异常情况,这时候就需要用失败重试策略重新调用 API 接口来获取。


在分布式系统中,为了保证数据分布式事务的强一致性,大家在调用RPC接口或者发送MQ时,针对可能会出现网络抖动请求超时情况采取一下重试操作。 大家用的最多的重试方式就是MQ了,但是如果你的项目中没有引入MQ,那就不方便了。




重试策略的介绍和限制


重试策略在服务治理方面也有很广泛的使用,通过定时检测,来查看服务是否存活。


重试是有场景限制的,不是什么场景都适合重试,比如参数校验不合法、写操作等(要考虑写是否幂等)都不适合重试。


  • 远程调用超时、网络突然中断可以重试。在微服务治理框架中,通常都有自己的重试与超时配置,比如dubbo可以设置retries=1,timeout=500调用失败只重试1次,超过500ms调用仍未返回则调用失败。




重试的场景有哪些?


外部 RPC 调用,或者数据入库等操作,如果一次操作失败,可以进行多次重试,提高调用成功的可能性。




常用的重试框架


  • Spring异常重试框架Spring Retry:Spring Retry支持集成到Spring或者Spring Boot项目中,而它支持AOP的切面注入写法,所以在引入时必须引入aspectjweaver.jar包。
  • sisyphus 综合了 spring-retry 和 gauva-retrying 的优势,使用起来也非常灵活。
  • github.com/houbb/sisyp…
  • guava-retrying 模块提供了一种通用方法, 可以使用Guava谓词匹配增强的特定停止、重试和异常处理功能来重试任意Java代码。




spring-retry的重试机制


Spring Retry 为 Spring 应用程序提供了声明性重试支持。 它用于Spring批处理、Spring集成、Apache Hadoop(等等)的Spring。


This is a small extension to Google’s Guava library to allow for the creation of configurable retrying strategies for an arbitrary function call, such as something that talks to a remote service with flaky uptime.



Maven配置

image.png



启用重试功能


启动类上面添加@EnableRetry注解,启用重试功能,或者在使用retry的service上面添加也可以,或者Configuration配置类上面。建议所有的Enable配置加在启动类上,可以清晰地统一管理使用的功能。

image.png


添加@Retryable和@Recover注解


@Retryable注解,被注解的方法发生异常时会重试


  • value:指定发生的异常进行重试
  • include:和value一样,默认空,当exclude也为空时,所有异常都重试
  • exclude:指定异常不重试,默认空,当include也为空时,所有异常都重试
  • maxAttemps:重试次数,默认3
  • backoff:重试补偿机制,默认没有


@Backoff注解


  • delay:指定延迟后重试
  • multiplier:指定延迟的倍数,比如delay=5000l,multiplier=2时,第一次重试为5秒后,第二 次为10秒,第三次为20秒



@Recover注解


当重试到达指定次数时,被注解的方法将被回调,可以在该方法中进行日志处理。需要注意的是发生的异常和入参类型一致时才会回调。

image.png


guava-retry


guava-retrying 模块提供了一种通用方法, 可以使用Guava谓词匹配增强的特定停止、重试和异常处理功能来重试任意Java代码。

image.png

guava-retry的Git地址


github.com/rholder/gua…



优势


guava retryer工具与spring-retry类似,都是通过定义重试者角色来包装正常逻辑重试,但是Guava retryer有更优的策略定义,在支持重试次数和重试频度控制基础上,能够兼容支持多个异常或者自定义实体对象的重试源定义,让重试功能有更多的灵活性。


Guava Retryer也是线程安全的,入口调用逻辑采用的是 java.util.concurrent.Callable 的 call() 方法,遇到异常之后,重试 3 次停止

public static void main(String[] args) {
    Callable<Boolean> callable = new Callable<Boolean>() {
        @Override
        public Boolean call() throws Exception {
            // do something useful here
            LOGGER.info("call...");
            throw new RuntimeException();
        }
    };
    Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
            .retryIfResult(Predicates.isNull())
            .retryIfExceptionOfType(IOException.class)
            .retryIfRuntimeException()
            .withStopStrategy(StopStrategies.stopAfterAttempt(3))
            .build();
    try {
        retryer.call(callable);
    } catch (RetryException | ExecutionException e) {
        e.printStackTrace();
    }
}
复制代码

其主要接口及策略介绍


  • Attempt:一次执行任务;
  • AttemptTimeLimiter:单次任务执行时间限制(如果单次任务执行超时,则终止执行当前任务);
  • BlockStrategies:任务阻塞策略(通俗的讲就是当前任务执行完,下次任务还没开始这段时间做什么……- - BlockStrategies.THREAD_SLEEP_STRATEGY 也就是调用 Thread.sleep(sleepTime);
  • RetryException:重试异常;
  • RetryListener:自定义重试监听器,可以用于异步记录错误日志;
  • StopStrategy:停止重试策略,提供三种:
  • StopAfterDelayStrategy:设定一个最长允许的执行时间;比如设定最长执行10s,无论任务执行次数,只要重试的时候超出了最长时间,则任务终止,并返回重试异常RetryException;
  • NeverStopStrategy:不停止,用于需要一直轮训直到返回期望结果的情况;
  • StopAfterAttemptStrategy:设定最大重试次数,如果超出最大重试次数则停止重试,并返回重试异常;
  • WaitStrategy:等待时长策略(控制时间间隔),返回结果为下次执行时长:
  • FixedWaitStrategy:固定等待时长策略;
  • RandomWaitStrategy:随机等待时长策略(可以提供一个最小和最大时长,等待时长为其区间随机值)
  • IncrementingWaitStrategy:递增等待时长策略(提供一个初始值和步长,等待时间随重试次数增加而增加)
  • ExponentialWaitStrategy:指数等待时长策略;
  • FibonacciWaitStrategy :Fibonacci 等待时长策略;
  • ExceptionWaitStrategy :异常时长等待策略;
  • CompositeWaitStrategy :复合时长等待策略;



根据结果判断是否重试


使用场景:如果返回值决定是否要重试。



重试接口

image.png

image.png

重试策略设定无限重试


使用场景:在有异常情况下,无限重试(默认执行策略),直到返回正常有效结果;

image.png


根据异常判断是否重试


使用场景:根据抛出异常类型判断是否执行重试。

image.png

image.png



等待策略——设定重试等待固定时长策略


使用场景:设定每次重试等待间隔固定为10s;

image.png

等待策略——设定重试等待时长固定增长策略


场景:设定初始等待时长值,并设定固定增长步长,但不设定最大等待时长;


例如:调用间隔时间递增1秒:

image.png



重试框架的总结


优雅重试共性和原理


  • 正常和重试优雅解耦,重试断言条件实例或逻辑异常实例是两者沟通的媒介,还有一种方式,是开发者自己编写重试机制,但是大多不够优雅
  • 约定重试间隔,差异性重试策略,设置重试超时时间,进一步保证重试有效性以及重试流程稳定性。


  • 都使用了命令设计模式,通过委托重试对象完成相应的逻辑操作,同时内部封装实现重试逻辑。
  • spring-retry 和 guava-retry 工具都是线程安全的重试,能够支持并发业务场景的重试逻辑正确性。两种方式都是比较优雅的重试策略,Spring-retry配置更简单,实现的功能也相对简单,Guava本身就是谷歌推出的精品java类库,guava-retry也是功能非常强大,相比较于Spring-Retry在是否重试的判断条件上有更多的选择性,可以作为Spring-retry的补充。



优雅重试适用场景


功能逻辑中存在不稳定依赖场景,需要使用重试获取预期结果或者尝试重新执行逻辑不立即结束。比如远程接口访问,数据加载访问,数据上传校验等等。


对于异常场景存在需要重试场景,同时希望把正常逻辑和重试逻辑解耦。


对于需要基于数据媒介交互,希望通过重试轮询检测执行逻辑场景也可以考虑重试方案。




相关文章
|
1月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
1月前
|
安全 前端开发 Java
《深入理解Spring》:现代Java开发的核心框架
Spring自2003年诞生以来,已成为Java企业级开发的基石,凭借IoC、AOP、声明式编程等核心特性,极大简化了开发复杂度。本系列将深入解析Spring框架核心原理及Spring Boot、Cloud、Security等生态组件,助力开发者构建高效、可扩展的应用体系。(238字)
|
1月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
354 2
|
1月前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
121 8
|
1月前
|
监控 Cloud Native Java
Spring Boot 3.x 微服务架构实战指南
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Spring Boot 3.x与微服务架构,探索云原生、性能优化与高可用系统设计。以代码为笔,在二进制星河中谱写极客诗篇。关注我,共赴技术星辰大海!(238字)
Spring Boot 3.x 微服务架构实战指南
|
2月前
|
安全 Java Ruby
我尝试了所有后端框架 — — 这就是为什么只有 Spring Boot 幸存下来
作者回顾后端开发历程,指出多数框架在生产环境中难堪重负。相比之下,Spring Boot凭借内置安全、稳定扩展、完善生态和企业级支持,成为构建高可用系统的首选,真正经受住了时间与规模的考验。
253 2
消息中间件 Java Kafka
199 0
|
1月前
|
Java 测试技术 数据库连接
【SpringBoot(四)】还不懂文件上传?JUnit使用?本文带你了解SpringBoot的文件上传、异常处理、组件注入等知识!并且带你领悟JUnit单元测试的使用!
Spring专栏第四章,本文带你上手 SpringBoot 的文件上传、异常处理、组件注入等功能 并且为你演示Junit5的基础上手体验
820 2

热门文章

最新文章

下一篇
oss云网关配置