Spring 框架——@Retryable 注解与 @Recover 注解

目录

1.@Retryable 注解介绍

(1)@Retryable 注解用于实现方法的重试机制,通常用于处理暂时性的失败,确保在特定条件下自动重试方法调用。它常用于微服务和分布式系统中,特别是在处理网络请求或外部服务时。@Retryable 注解的代码如下所示:

package org.springframework.retry.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Retryable {
    String recover() default "";

    String interceptor() default "";

    /** @deprecated */
    @Deprecated
    Class<? extends Throwable>[] value() default {};

    /** @deprecated */
    @AliasFor("retryFor")
    @Deprecated
    Class<? extends Throwable>[] include() default {};

    @AliasFor("include")
    Class<? extends Throwable>[] retryFor() default {};

    /** @deprecated */
    @Deprecated
    @AliasFor("noRetryFor")
    Class<? extends Throwable>[] exclude() 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 {};
}

(2)@Retryable 注解的常用属性如下:

  • value:指定要重试的异常类型。可以是多个异常类型,例如 @Retryable(value = {IOException.class, TimeoutException.class})
  • retryFor:另一个方式指定需要重试的异常类型。用法与 value 类似。
  • include:指定哪些异常类型应该被包含在重试范围内。与 value 类似,但用于更精确的控制重试条件。
  • exclude:指定不需要重试的异常类型。例如,@Retryable(exclude = {NullPointerException.class}) 表示遇到 NullPointerException 时不进行重试。
  • maxAttempts:设置最大重试次数。例如,@Retryable(maxAttempts = 5) 表示最多重试 5 次。
  • backoff:配置重试间隔和增量。可以使用 Backoff 类中的属性来设定,例如 @Retryable(backoff = @Backoff(delay = 2000, multiplier = 2)) 表示重试延迟初始为 2000 毫秒,每次重试间隔都会是上一次的两倍。
  • recover:用于指定在所有重试尝试失败后,调用的恢复方法。这个方法通常用于处理最终失败后的逻辑,例如清理资源或记录错误。具体用法见后续的 @Recover 注解与 @Retryable 注解配合使用。

2.示例:如何使用 @Retryable 注解

假设你有一个服务类 PaymentService,其方法 processPayment 可能会因临时网络问题而失败。你希望在遇到网络异常时自动重试几次。

2.1.添加依赖

首先,确保你的 Spring 项目中包含了 spring-retryspring-boot-starter-aop 依赖。在 pom.xml 中添加如下内容:

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.2.启用重试功能

在你的 Spring Boot 应用程序中,使用 @EnableRetry 注解来启用重试功能:

import org.springframework.retry.annotation.EnableRetry;
import org.springframework.stereotype.Service;

@EnableRetry
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

2.3.使用 @Retryable 注解

在需要进行重试的方法上添加 @Retryable 注解。例如,假设 processPayment 方法需要重试:

import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

@Service
public class PaymentService {

    @Retryable(
        value = {PaymentException.class}, // 需要重试的异常类型
        maxAttempts = 5, 	//最大重试次数为 5
        backoff = @Backoff(delay = 2000, multiplier = 2, maxDelay = 100000) //重试策略:重试延迟初始为 2000 毫秒,每次重试间隔都会是上一次的两倍,最大重试延迟为 100000 毫秒
    )
    public void processPayment() throws PaymentException {
        //模拟处理支付的逻辑,如果发生 PaymentException 则会进行重试
        if (someConditionFails()) {
            throw new PaymentException("Payment processing failed.");
        }
        //处理支付成功
    }
    
    private boolean someConditionFails() {
        //模拟条件失败
        return true;
    }
}

2.4.解释

(1)@Retryable 注解的三个属性解释如下:

  • value: 指定需要重试的异常类型,这里是 PaymentException。如果方法抛出这种异常,将会触发重试机制。
  • maxAttempts: 指定最大重试次数,这里设置为 5 次。
  • backoff: 配置重试的间隔和增量。这包括 delay(首次重试的延迟时间),multiplier(每次重试时延迟时间的倍数增长),maxDelay(最大延迟时间)。

(2)在这个例子中,如果 processPayment 方法抛出 PaymentException,系统将自动重试最多 5 次,每次重试之间的间隔将从 2 秒开始,并且每次重试时延迟时间会加倍,直到达到最大延迟时间。

3.@Recover 注解介绍

@Recover 注解用于定义当所有重试尝试失败后要执行的恢复逻辑。它通常与 @Retryable 注解配合使用,它所标记的方法会在 @Retryable 注解的方法所有重试失败后被调用,处理最终的错误恢复,例如记录日志、通知用户或执行补救措施。其代码如下所示:

package org.springframework.retry.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({RetryConfiguration.class})
@Documented
public @interface Recover {

}

4.示例:@Recover 注解与 @Retryable 注解配合使用

4.1.两者配合使用

(1)添加依赖与启用重试功能(与 2.1 和 2.2 相同)。

(2)示例代码如下:

package com.example.myexample.service.impl;

import com.example.myexample.service.AnnotationTestService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

import java.io.IOException;

@Slf4j
@Service
public class AnnotationTestServiceImpl implements AnnotationTestService {
    //定义一个需要重试的方法
    @Override
    @Retryable(
            recover = "recover",
            value = IOException.class,
            maxAttempts = 3,
            backoff = @Backoff(delay = 2000))
    public void performOperation() throws IOException {
        log.info("Attempting operation...");
        // 这里抛出一个可能导致重试的异常
        throw new IOException("Operation failed!");
    }

    //定义一个 @Recover 方法来处理所有重试失败后的恢复逻辑
    @Recover
    public void recover(IOException e) {
        log.info("All retries failed. Recovering from exception: " + e.getMessage());
        // 执行恢复逻辑,如记录日志、通知用户、清理资源等
    }
}

(3)解释

  • performOperation 方法使用了 @Retryable 注解,指定了在遇到 IOException 时会进行最多 3 次重试,每次重试之间间隔 2000 毫秒。
  • 如果 performOperation 方法在所有重试尝试后仍然抛出 IOException,此时 @Retryable 注解中的 recover 属性指定的方法(即 recover 方法)将被调用。recover 方法处理这些失败后的恢复操作,比如记录错误信息或通知用户。

(4)输出结果如下:

2024-09-09 23:23:49.282  INFO 2668 --- [nio-8080-exec-2] c.e.m.s.impl.AnnotationTestServiceImpl   : Attempting operation...
2024-09-09 23:23:51.292  INFO 2668 --- [nio-8080-exec-2] c.e.m.s.impl.AnnotationTestServiceImpl   : Attempting operation...
2024-09-09 23:23:53.294  INFO 2668 --- [nio-8080-exec-2] c.e.m.s.impl.AnnotationTestServiceImpl   : Attempting operation...
2024-09-09 23:23:53.295  INFO 2668 --- [nio-8080-exec-2] c.e.m.s.impl.AnnotationTestServiceImpl   : All retries failed. Recovering from exception: Operation failed!

注:上面只展示了核心代码,并未展示其它代码(例如 Controller 层代码)。

4.2.两者对应关系

(1)@Retryable 注解与 @Recover 注解的对应关系如下:

  • 异常匹配:@Recover 注解的方法的参数类型必须与 @Retryable 注解的方法抛出的异常类型一致。这样,Spring 能够将最终失败的异常传递到对应的 @Recover 方法中(否则会提示报错)。在上述例子中,performOperation 方法抛出的异常类型与 recover 方法的参数类型均为 IOException
  • 方法名和数量:每个 @Retryable 注解的方法通常对应一个 @Recover 方法。如果有多个 @Retryable 方法,则每个方法需要有一个匹配的 @Recover 方法来处理相应的异常。

(2)recover 属性可省略:以上述例子来说,只要 @Recover 注解的方法的参数类型与 @Retryable 注解的方法抛出的异常类型一致,即使不设置 recover 属性,当 performOperation 方法重试全部失败后,recover 方法也会被执行

5.其他注意事项

(1)使用 @Retryable 注解时,需要注意以下几点:

  • 异常类型:确保指定的 valueincluderetryFor 属性中的异常类型与你的代码实际抛出的异常一致。
  • 性能影响:频繁重试可能会对系统性能产生影响,因此合理配置 maxAttemptsbackoff
  • 幂等性:确保被重试的方法是幂等的,即多次执行不会引起副作用。
  • 超时设置:配置合理的重试超时时间和间隔,避免重试导致的长时间等待。
  • 正确配置 backoff:使用合理的 delaymultiplier,以避免过于频繁的重试或间隔过长。

(2)@Retryable 注解与 @Recover 注解配合使用,需要注意:一般建议一个 @Retryable 注解方法对应一个 @Recover 注解方法,并且通过设置 @Retryable 注解中的 recover 属性来指定对应的恢复方法。因为当多个 @Recover 注解方法的参数类型与一个 @Retryable 注解方法抛出的异常类型相同时,可能难会产生一些不确定的问题。

(3)例如,下面的代码并未设置 @Retryable 注解的 recover 属性,那么按照上面的对应规则,recover1 方法和 recover2 方法均为 performOperation 方法的恢复方法,那么当 performOperation 方法全部重试失败后,到底哪个恢复方法会执行呢?还是都执行呢?

@Slf4j
@Service
public class AnnotationTestServiceImpl implements AnnotationTestService {
    //定义一个需要重试的方法
    @Override
    @Retryable(value = IOException.class, maxAttempts = 3, backoff = @Backoff(delay = 2000))
    public void performOperation() throws IOException {
        log.info("Attempting operation...");
        // 这里抛出一个可能导致重试的异常
        throw new IOException("Operation failed!");
    }

    //定义一个 @Recover 方法来处理所有重试失败后的恢复逻辑
    @Recover
    public void recover2(IOException e) {
        log.info("All retries failed. Recovering from exception2: " + e.getMessage());
        // 执行恢复逻辑,如记录日志、通知用户、清理资源等
    }
    
	//定义一个 @Recover 方法来处理所有重试失败后的恢复逻辑
    @Recover
    public void recover1(IOException e) {
        log.info("All retries failed. Recovering from exception1: " + e.getMessage());
        // 执行恢复逻辑,如记录日志、通知用户、清理资源等
    }
}

测试后的结果如下:

在这里插入图片描述

显然是 recover1 方法生效了,而 recover2 方法并未生效。具体原因,本人并未深入了解(本人猜测与方法名的字典顺序有关,排在前面的先执行),有明白原因的读者可以在评论区留言。

那如果在 @Retryable 注解中设置属性 recover = “recover2”,结果则会与上面相反,如下图所示:

在这里插入图片描述

(4)所以为了避免出现上述情况,这里建议当 @Retryable 注解与 @Recover 注解配合使用时,最好通过 @Retryable 注解的 recover 属性来设置对应的恢复方法,并且 @Recover 注解的方法的参数类型必须与 @Retryable 注解的方法抛出的异常类型保持一致

<think>好的,我现在需要解释一下@Retryable注解的作用以及它的参数。首先,我应该回忆一下这个注解是在什么框架或库中使用的。记得Spring Retry是一个提供重试机制的库,而@Retryable就是其中的一个注解,用来标记方法在遇到异常时自动重试。 接下来,用户可能想知道它的主要作用。需要明确说明它用于方法级别的重试,当方法抛出指定异常时,框架会自动重试该方法。这样可以增强系统的容错能力,特别是在处理网络不稳定、临时故障等情况时非常有用。 然后是参数部分。我需要列出常用的参数并逐一解释。例如,value或include指定哪些异常会触发重试,exclude排除哪些异常,maxAttempts设置最大尝试次数,backoff配置退避策略,包括delay、multiplier和maxDelay等。可能还有recover方法,指定重试失败后的恢复方法。 需要确保每个参数都解释清楚,并且给出例子,比如使用maxAttempts=3表示最多试三次。同时,提醒参数之间的一些关联,比如backoff中的参数如何影响等待时间。 另外,用户可能想知道如何结合@Recover注解来定义恢复逻辑。这部分需要补充说明,当所有重试失败后,调用指定的恢复方法处理异常。 最后,可能还需要提到使用@Retryable的前提条件,比如在Spring项目中需要启用@EnableRetry,并添加相关依赖。确保用户知道如何正确配置和使用这个注解。 检查是否有遗漏的重要参数或常见用法,确保信息准确,没有语法错误。可能需要参考Spring Retry的官方文档,确认参数的名称和功能是否正确。比如,backoff的delay和maxDelay是否准确,multiplier的作用是否正确描述。 总结一下,回答的结构应该是先介绍作用,再详细说明各个参数,最后补充使用注意事项和示例,确保用户能够理解并正确应用这个注解。</think>### @Retryable 注解的作用 `@Retryable` 是 Spring Retry 模块提供的一个注解,用于在方法执行失败时**自动重试**,增强系统的容错性。它通常用于处理可能因临时故障(如网络波动、资源忙等)导致的异常场景,通过重试机制提高操作的成功率。 --- ### 核心参数详解 以下是 `@Retryable` 的主要参数及其用途: | 参数 | 说明 | 示例 | |------|------|------| | **`value` 或 `include`** | 指定触发重试的异常类型(可多个) | `@Retryable(value = {IOException.class, SQLException.class})` | | **`exclude`** | 明确排除不触发重试的异常类型 | `@Retryable(exclude = {NullPointerException.class})` | | **`maxAttempts`** | 最大重试次数(含首次调用) | `@Retryable(maxAttempts = 3)` → 最多尝试3次 | | **`backoff`** | 配置重试的**退避策略**(延迟时间、倍数等) | 需结合 `@Backoff` 注解使用(见下文) | #### **`backoff` 退避策略参数** 通过 `@Backoff` 注解配置重试的等待时间策略: - **`delay`**: 初始延迟时间(单位:毫秒) `@Backoff(delay = 1000)` → 第一次重试前等待1秒 - **`multiplier`**: 延迟时间倍数(基于上一次延迟) `@Backoff(delay = 1000, multiplier = 2)` → 第一次1秒,第二次2秒,第三次4秒... - **`maxDelay`**: 最大延迟时间(避免无限增长) `@Backoff(delay = 1000, multiplier = 2, maxDelay = 5000)` → 延迟不超过5秒 #### **`recover`** 参数 指定重试全部失败后的**兜底方法**(需配合 `@Recover` 注解使用): ```java @Retryable(recover = "recoverMethod") public void doSomething() { ... } @Recover public void recoverMethod(Exception e) { // 处理最终失败逻辑 } ``` --- ### 使用示例 ```java @Retryable( value = {RemoteAccessException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 2, maxDelay = 5000) ) public void callExternalService() { // 调用可能失败的外部服务 } ``` --- ### 注意事项 1. **启用 Spring Retry** 需在配置类添加 `@EnableRetry` 注解以启用重试功能: ```java @Configuration @EnableRetry public class AppConfig { ... } ``` 2. **依赖引入** Maven 项目中需添加依赖: ```xml <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> ``` 3. **幂等性要求** 重试的方法需保证**幂等性**(多次执行结果一致),避免因重试导致数据不一致。 通过合理配置 `@Retryable`,可以有效提升系统在分布式环境下的健壮性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值