最近研究openfeign的重试原理时,发现其依赖spring-retry框架,不禁好奇并测试一二。使用步骤如下:

1、添加pom.xml依赖(springboot版本为2.6.14)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.6.14</version>
</dependency>
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <revision>1.3.4</revision>
</dependency>

2、启用重试框架,启动函数上添加注解 @EnableRetry

3、在方法上添加重试注解 @Retryable

@Retryable(maxAttempts = 3, recover = "recoverTest1", listeners = "customRetryListener", backoff = @Backoff(delay = 2000, multiplier = 2))
public Boolean test1(String user) {
    log.info("执行操作,参数user={}", user);
    if (Objects.equals(user, "张三")) {
        throw new RuntimeException("重试测试");
    }
    return Boolean.TRUE;
}

注解主要参数说明:

  • maxAttempts:最大尝试次数(包含第一次)
  • recover:重试全部失败后调用回退方法名,回退放假
  • listeners:监听类,值为Bean名称
  • backoff:重试等待策略
  • backoff.delay:延迟基数,默认1000(1秒)
  • backoff.multiplier:延迟倍数,默认0(忽略设置),本次延迟=上次延迟*multiplier。如delay=2000&multiplier=3时,第一次重试间隔2秒,第二次重试间隔6秒,第三次重试间隔18秒。参考ExponentialBackOffPolicy.backOff
  • backoff.maxDelay:最大延迟时间,默认0(忽略设置)

4、添加回退方法,方法名上增加注解 @Recover,第一个入参为异常信息,其他入参和出参跟操作方法一致

@Recover
private Boolean recoverTest1(RuntimeException e, String user) {
    log.warn("操作回退,错误信息:{}。参数user={}", e.getMessage(), user);
    return Boolean.FALSE;
}

 5、重试监听

@Slf4j
@Component
public class CustomRetryListener implements RetryListener {
    /**
     * 只在首次调用前执行
     */
    @Override
    public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
        log.info("尝试已打开,当前重试次数:{}", context.getRetryCount());
        return true;
    }

    /**
     * 只在重试次数用完或调用成功后执行
     */
    @Override
    public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable e) {
        log.info("尝试已关闭,当前尝试次数:{}", context.getRetryCount());
    }

    /**
     * 每次调用发生异常时执行
     */
    @Override
    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable e) {
        log.warn("第{}次尝试失败,异常信息:{}", context.getRetryCount(), e.getMessage());
    }
}

6、异步方法同样可以重试

@Async
@Retryable(maxAttempts = 2, recover = "recoverTest2", backoff = @Backoff(delay = 1000))
public void test2(String user) {
    log.info("执行操作,参数user={}", user);
    if (Objects.equals(user, "张三")) {
        throw new RuntimeException("重试测试");
    }
}

@Recover
private void recoverTest2(RuntimeException e, String user) {
    log.warn("操作回退,异常信息:{}。参数user={}", e.getMessage(), user);
}

7、使用RetryTemplate

定义RetryTemplate

@Bean
public RetryTemplate retryTemplate(CustomRetryListener retryListener) {
    // 重试策略为ExponentialBackOffPolicy
    return RetryTemplate.builder().exponentialBackoff(2000, 2, 10000).withListener(retryListener).build();
}

使用RetryTemplate

@Autowired
private RetryTemplate retryTemplate;

public String test3() {
    String result = retryTemplate.execute(new RetryCallback<String, RuntimeException>() {
        @Override
        public String doWithRetry(RetryContext context) throws RuntimeException {
            log.info("当前尝试次数:{}", context.getRetryCount());
            throw new RuntimeException("重试测试");
        }
    }, new RecoveryCallback<String>() {
        @Override
        public String recover(RetryContext context) throws Exception {
            log.info("操作失败,执行回退");
            return "error";
        }
    });
    return result;
}

 

posted on 2023-03-08 14:27  玄同太子  阅读(339)  评论(0编辑  收藏  举报