文章目录
1 . 简介
两个注解一般配合使用,通常用于解决由于网络、数据库、文件系统等原因导致的临时性错误,以提高应用的健壮性和可用性。
@Retryable
和@Recover
是Spring Framework中的注解,用于支持在方法执行期间发生异常时的重试和恢复操作。@Retryable
注解用于标记方法,在方法执行期间发生异常时进行重试。重试行为可以使用Spring Retry框架提供的默认策略或自定义策略来定义。我们可以指定要重试的异常类型以及最大重试次数和重试间隔等参数。@Recover
注解用于标记一个恢复方法,在最终重试失败后执行该方法,降级处理。恢复方法应尽量具有与重试方法相同的参数和返回类型(提一嘴:两个方法的返回值类型必须相同,捕获异常类型和入参可以不同,后面给出实例),并且应在同一类中声明。如果未指定恢复方法,则重试失败后将抛出最后一次异常。
2 . 实例:分母=0异常,导致重试(所有实例中fenzi=5,fenmu=0)
想要使用这两个注解,首先需引入依赖
spring-retry
,再启动类上添加注解@EnableRetry
;
/**
* @description: 测试重试和恢复注解,入参写死,fenzi=5, fenmu=0
* @param:
* @return:
* @author name silk
* @date: 2024/1/30 21:11
*/
@GetMapping("/retry")
public String testRetryableAndRecover() {
return testService.testRetryable(5, 0);
}
2.1 重试方法,恢复方法有一致的入参类型、返回值类型;重试方法抛出异常与恢复方法异常类型一致;(成功)
/***
* @description: 测试注解@Retryable, 重试方法
* @param: fenzi fenmu
* @return:
* @author name silk
* @date: 2024/1/30 19:51
*/
@Retryable(value = Exception.class, maxAttempts = 3)
public String testRetryable(int fenzi, int fenmu) {
int res;
res = fenzi / fenmu;
return "the calculate result is : " + res;
}
/***
* @description: 测试恢复方法,降级处理
* @param: e fenzi fenmu
* @return:
* @author name silk
* @date: 2024/1/30 19:51
*/
@Recover
public String testRecover(Exception e, int fenzi, int fenmu) {
log.info("调用恢复方法,做降级处理...");
return "downgrade process over.";
}
在上面的示例中,testRetryable()
方法标记为重试,当Exception异常发生时最多重试3次。如果在最终重试失败后,testRetryable()
方法将调用testRecover()
方法进行恢复操作,并将最后一次重试的异常作为参数传递给testRecover()
方法。
2.2 重试方法,恢复方法有一致的返回值类型;入参类型、异常类型不一致;(成功)
/***
* @description: 测试注解@Retryable, 重试方法
* @param: fenzi fenmu
* @return:
* @author name silk
* @date: 2024/1/30 19:51
*/
@Retryable(value = Exception.class, maxAttempts = 3)
public String testRetryable(int fenzi, int fenmu) {
int res;
res = fenzi / fenmu;
return "the calculate result is : " + res;
}
/***
* @description: 测试恢复方法,降级处理
* @param: e fenzi fenmu
* @return:
* @author name silk
* @date: 2024/1/30 19:51
*/
@Recover
public String testRecover(ArithmeticException e) {
log.info("调用恢复方法,做降级处理...");
return "downgrade process over.";
}
根据上述实例看,虽然实例2这种情况能够成功,但是建议开发过程中最好按照第一张情况各参数保持一致。
2.3 扩展:一个类中有多个@Recover和@Retryable怎么区分
如果一个类中有多个方法标记了@Retryable
和@Recover
注解,我们可以通过value
属性来区分它们。value
属性允许指定一个异常类型的数组,以区分在方法执行期间抛出的不同异常类型。
@Service
public class TestService {
@Retryable(value = {IOException.class})
public void methodA() throws IOException {
// Some code that may throw an IOException
}
@Recover
public void recoverA(IOException e) {
// Recovery logic for methodA() goes here
}
@Retryable(value = {NullPointerException.class})
public void methodB() throws NullPointerException {
// Some code that may throw a NullPointerException
}
@Recover
public void recoverB(NullPointerException e) {
// Recovery logic for methodB() goes here
}
}
在上面的示例中,methodA()
和methodB()
方法都标记为@Retryable
注解,以便在抛出IOException
或NullPointerException
异常时进行重试。然后对于每个方法,都定义了一个恢复方法recoverA()
和recoverB()
,分别提供特定于该方法的恢复逻辑。由于每个方法都在@Retryable
注解的value
属性中指定了不同的异常类型,因此Spring框架可以区分它们,并在适当的时候调用相应的方法。
2.4 扩展:如果上述情况的异常类型也一致怎么区分
如果多个方法在抛出相同的异常时都需要进行重试和恢复操作,我们可以在每个方法上使用相同的@Retryable
和@Recover
注解。在这种情况下,Spring框架会自动根据需要调用相应的恢复方法。
在@Recover
注解标记的恢复方法中,可以通过方法的参数获取抛出异常的方法和异常信息。具体来说,@Recover
方法可以接受与@Retryable
注解标记的方法相同的参数,以便在恢复操作中访问异常信息和方法参数。
/***
* @description: 测试注解@Retryable, 重试方法
* @param: fenzi fenmu
* @return:
* @author name silk
* @date: 2024/1/30 19:51
*/
@Retryable(value = Exception.class, maxAttempts = 3)
public String testRetryable(Integer fenzi, Integer fenmu) {
Integer res;
res = fenzi / fenmu;
return "the calculate result is : " + res;
}
@Retryable(value = Exception.class, maxAttempts = 3)
public String testRetryable(String fenzi, String fenmu) {
Integer res;
res = Integer.getInteger(fenzi) / Integer.parseInt(fenmu);
return "the calculate result is : " + res;
}
/***
* @description: 测试恢复方法,降级处理
* @param: e fenzi fenmu
* @return:
* @author name silk
* @date: 2024/1/30 19:51
*/
@Recover
public String testRecover(Exception e, Object fenzi, Object fenmu) {
if (fenzi instanceof Integer) {
log.info("重试方法入参类型是Integer,调用恢复方法,做降级处理...");
return "integer === downgrade process over.";
} else if (fenzi instanceof String) {
log.info("重试方法入参类型是String,调用恢复方法,做降级处理...");
return "string === downgrade process over.";
}
log.info("调用恢复方法,做降级处理...");
return "downgrade process over.";
}
在上面的示例中,methodA()
和methodB()
方法都标记为@Retryable
注解,以便在抛出Exception异常时进行重试。在@Recover
注解标记的恢复方法中,可以通过方法的参数访问抛出异常的方法和方法参数。在上述示例中,我们检查第一个参数的类型以确定是哪个方法抛出了异常,从而提供相应的恢复逻辑。
3 . 其他
(1)@Retryable
的backoff
参数定义了在重试操作之间使用的退避策略,目前支持5种策略:固定间隔退避策略、指数退避策略,随机间隔退避策略、指数退避策略(带有最大退避时间)、自定义退避策略。其中要自定义一个退避策略类,需要实现org.springframework.retry.backoff.BackOffPolicy
接口,并实现其backOff()
方法,这里不展开介绍了。
(2)最后需要注意,使用@Retryable
注解会增加应用的复杂度,因为需要处理异常、定义退避策略等。在使用时需要权衡好可用性、性能和代码的复杂度,并根据具体的场景进行选择。