Spring @Retryable实现接口自动重试

本文介绍了如何在Spring框架中利用@Retryable注解实现接口请求的自动重试,包括解决参数问题、设定重试次数、配合@Recover处理补偿逻辑,以及相关注解参数解析。

Spring @Retryable实现接口自动重试

背景:
在实际项目中,我们在与其他第三方业务接口进行交互时,可能会因为一些网络波动导致超时失败,但是并不能只一次失败就判定接口请求失败,应该考虑重试多次后如果仍然失败,才返回请求失败。

解决方案:
解决方案一:try-catch 简单重试
通过判断返回结果或监听异常判定是否重试,同时为了解决立即重试的无效执行(假设异常是有外部执行不稳定导致的),休眠一定延迟时间重新执行功能逻辑。
在这里插入图片描述
但是这样会存在一个问题,那就是如果是请求参数不正常,那么就会一直重试下去,变成了死循环。
在这里插入图片描述

这样肯定不是我们想要的解决方案,所以我们需要添加限制,判断重试多少次以后仍然失败,就返回失败结果。
在这里插入图片描述

然后这次再看执行结果,这次就按照我们设置的次数进行重试了,已经达到了业务需求

在这里插入图片描述

结论:try-catch再加上提前设定重试次数,这样已可以满足我们重试机制了,但是有没有更简单的方法呢?答案是肯定的。

解决方案二:Spring @Retryable 注解实现
@Retryable是Spring提供的可重试注解,为了使用spring提供的重试机制

在pom文件中添加相应的依赖

<!-- spring retry 自动重试机制-->
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.2.4.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>
<!-- spring retry 自动重试机制-->

启动类添加注解

@EnableRetry

在需要重试的方法上添加注解@Retryable

@Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000L, multiplier = 1.5))

在这里插入图片描述

然后继续执行测试方法、结果发现执行了三次,然后输出请求失败语句。
在这里插入图片描述

但是只是这样并不满足我们实际业务,在实际业务中如果超过重试之后,一般需要记录本次失败原因,并且调用补偿方法进行业务处理,与消息推送,所以我们可以使用@Recover注解来实现超出重试次数后执行补偿方法。
在这里插入图片描述

但是这里需要注意的是,@Recover声明的方法必须与@Retryable声明的方法返回值一致,不然补偿方法会不生效。

以下是Spring自动重试注解的说明

@Retryable注解中的参数说明:

maxAttempts :最大重试次数,默认为3,如果要设置的重试次数为3,可以不写;
value:抛出指定异常才会重试
include:和value一样,默认为空,当exclude也为空时,默认所以异常
exclude:指定不处理的异常
backoff:重试等待策略,默认使用@Backoff@Backoff的value默认为1000L,我们设置为2000L。

@Backoff注解中的参数说明:

value:隔多少毫秒后重试,默认为1000L,我们设置为3000L;
delay:和value一样,但是默认为0;
multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,
则第一次重试为2秒,第二次为3秒,第三次为4.5秒。

如果对您有帮助 请点个关注,万分感谢

<think>首先,用户的问题是关于Spring框架中的@Transactional和@Retryable注解如何交互影响以及它们的作用顺序。用户提到了可能是在Spring框架下的使用场景。回顾系统级指令:-所有行内数学表达式必须使用$...$格式。-独立公式必须使用$$...$$格式并单独成段。-LaTeX语法正确。-使用中文回答。-生成相关问题。-在回答中引用的段落末尾自然地添加引用标识,例如[^1]。用户可见层的指令:-回答结构清晰,帮助用户逐步解决问题。-尽量保证回答真实可靠。参考站内引用:-引用[1]:关于@Version注解的说明。-引用[2]:关于@Transactional的坑,比如自调用问题。-引用[3]:关于SpringData的统一接口。用户的问题是:@Transactional和@Retryable注解是否会相互影响,以及作用顺序。我需要基于Spring框架的知识来回答。关键注解:-@Transactional:用于声明事务管理,确保方法在一个事务中执行。-@Retryable:可能来自SpringRetry模块,用于在方法失败时自动重试。在Spring中,@RetryableSpringRetry库的一部分,用于提供重试逻辑。SpringRetry可以与Spring的其他功能如事务管理集成。交互影响和作用顺序:1.**作用顺序**:当一个方法同时被@Transactional和@Retryable注解时,重试逻辑通常会在事务逻辑的外层。也就是说,重试会包裹事务。-在重试时,整个事务会被回滚并重试,因为事务是原子性的。-如果@Retryable注解在方法上,它会先触发重试机制;如果重试成功,事务提交;如果重试失败,事务回滚。2.**相互影响**:-**事务传播**:如果重试导致方法被多调用,事务传播行为(如REQUIRES_NEW)可能会影响事务边界。-**自调用问题**:类似引用[2]中的问题,如果@Transactional和@Retryable都在同一个类中,自调用可能绕过代理,导致注解不生效。解决方案是拆分到不同类或使用代理对象。-**异常处理**:@Retryable通常基于异常触发重试;@Transactional在异常时回滚事务。需要确保异常类型一致,避免冲突。结构回答:-**逐步解决问题**:1.解释每个注解的作用。2.描述它们的交互和顺序。3.讨论潜在问题(如自调用)。4.提供解决方案或最佳实践。5.总结。生成相关问题:-基于用户问题,生成2-3个相关问题,放在回答末尾。引用标识:-在回答中,如果引用站内内容,自然地添加[^n]。例如,提到自调用问题时,引用[^2]。确保中文回答。最终回答结构:-开头:直接回答问题。-主体:分步解释。-结尾:相关问题。相关问题想法:1.如何在Spring中配置@Retryable重试策略?2.@Transactional和@Retryable在分布式事务中的影响?3.如何避免注解在自调用时失效?现在,构建回答。</think>### Spring中@Transactional和@Retryable注解的交互影响及作用顺序 在Spring框架中,`@Transactional`(用于事务管理)和`@Retryable`(来自Spring Retry模块,用于方法失败时自动重试)是两个常用注解。它们可能相互影响,尤其是在同一个方法或类中使用时。下面我将逐步解释它们的作用顺序、交互机制、潜在问题及解决方案,确保回答基于Spring官方文档和最佳实践。 #### 1. **注解作用简述** - **@Transactional**:声明一个方法在事务中执行,确保ACID属性(原子性、一致性、隔离性、持久性)。当方法抛出未捕获的异常时,事务会回滚;否则提交[^2]。 - **@Retryable**:属于Spring Retry库,用于在方法执行失败(如抛出指定异常)时自动重试。例如: ```java @Retryable(maxAttempts = 3, backoff = @Backoff(delay = 1000)) public void retryableMethod() { ... } ``` #### 2. **作用顺序与交互机制** 在同一个方法上同时使用这两个注解时,它们的执行顺序是关键: - **作用顺序**:重试逻辑(`@Retryable`)包裹在事务逻辑(`@Transactional`)的外层。这意味着: - 当方法被调用时,先触发重试机制。 - 每重试尝试都会启动一个新的事务(如果事务传播行为设置为`Propagation.REQUIRES_NEW`)或重用现有事务(默认`Propagation.REQUIRED`)。 - 如果重试成功,事务提交;如果所有重试均失败,事务回滚。 数学上,这可以用事务边界和重试数的关系表示: 设重试数为$n$,每尝试的独立事务为$T_i$($i=1,2,\ldots,n$),则整个过程可建模为: $$ \text{Retry} \left( \bigcup_{i=1}^{n} T_i \right) $$ 其中,每个$T_i$是一个原子操作。 - **相互影响**: - **事务回滚与重试触发**:`@Retryable` 通常在方法抛出异常时触发重试,而 `@Transactional` 在异常时回滚事务。如果两个注解配置的异常类型一致(如`RuntimeException`),重试会正确触发,事务在每重试前回滚。 - **事务传播行为**:如果事务传播设置为`REQUIRES_NEW`,每重试会创建独立事务,避免脏数据问题。但默认的`REQUIRED`可能导致重试间共享事务状态,引发不一致风险。 - **性能影响**:重试会增加事务开销(如数据库锁竞争),在高并发场景下可能导致性能下降。 #### 3. **潜在问题与解决方案** - **问题1:自调用导致注解失效** 如果 `@Transactional` 和 `@Retryable` 注解在同一个类的方法中相互调用(如方法A调用方法B),由于Spring基于AOP代理实现,自调用会绕过代理,使注解无效[^2]。 **解决方案**: - 将注解方法拆分到不同类中(例如,将重试逻辑移到单独的Service类)。 - 使用代理对象调用:开启`@EnableAspectJAutoProxy(exposeProxy = true)`,然后在代码中调用`AopContext.currentProxy().methodB()`。 - **问题2:异常处理冲突** 如果 `@Retryable` 配置的重试异常(如`SQLException`)与 `@Transactional` 的回滚异常不匹配,可能导致重试未触发或事务未回滚。 **解决方案**: - 确保两个注解的异常类型一致,例如: ```java @Retryable(retryFor = {SQLException.class}) @Transactional(rollbackFor = SQLException.class) public void process() { ... } ``` - 使用Spring Retry的`Recover`机制处理最终失败。 - **问题3:事务隔离与重试竞争** 在重试过程中,如果多个线程竞争同一资源(如数据库行锁),可能引发死锁或脏读。 **解决方案**: - 优化事务隔离级别(如使用`@Transactional(isolation = Isolation.SERIALIZABLE)`)。 - 增加重试间隔(如`@Backoff(delay = 2000)`),减少竞争概率。 #### 4. **最佳实践总结** - **作用顺序优先**:始终让 `@Retryable` 包裹 `@Transactional`,避免在事务内重试(可能导致部分提交)。 - **测试验证**:使用Spring Test模块模拟异常,验证重试和事务行为。 - **依赖配置**:确保项目中添加Spring Retry依赖(Maven中`spring-retry`和`aspectjweaver`)。 - **性能权衡**:重试数(`maxAttempts`)不宜过高(一般3-5),避免事务长时间挂起。 通过合理配置,这两个注解可以协同工作:重试机制提升方法鲁棒性,事务保证数据一致性。但在复杂场景(如分布式事务),需额外考虑Seata等框架集成[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值