Skip to content

SpringRetry入门使用

本文基于spring-retry:1.2.5.RELEASE

依赖

java
<dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
        </dependency>

@Retryable

  • 在需要被重试的方法上加上@Retryable注解即可, 默认重试3次, 每次间隔1s, 如下案例
java
@Service
public class ErrorService {

    @Retryable
    public void throwRunEx() {
        System.out.println("throwRunEx executed ==========> " + System.currentTimeMillis());
        throw new RuntimeException("this is a test runtime exception!");
    }
}


public class RetryTest extends BaseTest {

    @Autowired
    private ErrorService errorService;

    @Test
    public void testRetry() {
        errorService.throwRunEx();
    }
}

// 执行结果, 可以看到方法被重试了3次, 每次间隔1s
throwRunEx executed ==========> 1686458391279
throwRunEx executed ==========> 1686458392292
throwRunEx executed ==========> 1686458393292

java.lang.RuntimeException: this is a test runtime exception!

	at com.pingan.lcloud.demo.service.ErrorService.throwRunEx(ErrorService.java:12)
  • 该注解支持指定配置, 可以指定异常类型, 重试次数, 以及延迟时间(毫秒)
java
    @Retryable(include = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 100))
//    @Retryable(include = Exception.class, maxAttemptsExpression = "${retry.maxAttempts}",
//            backoff = @Backoff(delayExpression = "${retry.maxDelay}"))
    public void throwRunEx(String arg) {
        System.out.printf("throwRunEx executed, arg: %s, time: %d%n", arg, System.currentTimeMillis());
        throw new RuntimeException("this is a test runtime exception!");
    }

@Recover

  • 当@Retryable因指定异常重试达到最大次数后, 会调@Recover方法(就不会再抛出异常了)
java
@Service
public class ErrorService {

    @Retryable(include = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 100))
//    @Retryable(include = Exception.class, maxAttemptsExpression = "${retry.maxAttempts}",
//            backoff = @Backoff(delayExpression = "${retry.maxDelay}"))
    public void throwRunEx(String arg) {
        System.out.printf("throwRunEx executed, arg: %s, time: %d%n", arg, System.currentTimeMillis());
        throw new RuntimeException("this is a test runtime exception!");
    }

    @Recover
    public void recover(Exception e, String arg) {
        System.out.printf("recover executed, error: %s, arg: %s, time: %d%n", e.getMessage(), arg, System.currentTimeMillis());
    }
}

// 执行结果
throwRunEx executed, arg: newBee, time: 1686459024642
throwRunEx executed, arg: newBee, time: 1686459025646
throwRunEx executed, arg: newBee, time: 1686459026649
recover executed, error: this is a test runtime exception!, arg: newBee, time: 1686459026649

RetryTemplate

  • spring-retry提供了RetryOperations接口来支持以api的形式使用该框架, RetryTemplate是RetryOperations的默认实现
  • RetryCallback即是每次重试时需要插入的业务逻辑
java
public interface RetryOperations {
    <T, E extends Throwable> T execute(RetryCallback<T, E> var1) throws E;

    <T, E extends Throwable> T execute(RetryCallback<T, E> var1, RecoveryCallback<T> var2) throws E;

    <T, E extends Throwable> T execute(RetryCallback<T, E> var1, RetryState var2) throws E, ExhaustedRetryException;

    <T, E extends Throwable> T execute(RetryCallback<T, E> var1, RecoveryCallback<T> var2, RetryState var3) throws E;
}

使用示例

java
// 定义RetryTemplate, 重试2次, 每次间隔100毫秒
@Configuration
public class RetryConfiguration {
    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();

        FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
        fixedBackOffPolicy.setBackOffPeriod(100l);
        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);

        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(2);
        retryTemplate.setRetryPolicy(retryPolicy);

        return retryTemplate;
    }
}

// 测试
    @Autowired
    private RetryTemplate retryTemplate;

    @Test
    public void testRetryTemplate() {
        retryTemplate.execute(retryContext -> {
            errorService.throwRunEx("newBee2");
            return null;
        });
    }

// 执行结果
throwRunEx executed, arg: newBee2, time: 1686460287112
throwRunEx executed, arg: newBee2, time: 1686460287226

java.lang.RuntimeException: this is a test runtime exception!

Listeners

  • 重试过程提供了监听机制, 可拓展监听器来实现更多功能, 如以下案例增加了日志打印
java
@Slf4j
public class DefaultListenerSupport extends RetryListenerSupport {

    @Override
    public <T, E extends Throwable> void close(RetryContext context,
                                               RetryCallback<T, E> callback, Throwable throwable) {
        log.info("onClose");
        super.close(context, callback, throwable);
    }

    @Override
    public <T, E extends Throwable> void onError(RetryContext context,
                                                 RetryCallback<T, E> callback, Throwable throwable) {
        log.info("onError");
        super.onError(context, callback, throwable);
    }

    @Override
    public <T, E extends Throwable> boolean open(RetryContext context,
                                                 RetryCallback<T, E> callback) {
        log.info("onOpen");
        return super.open(context, callback);
    }
}
java
// 需要注册
retryTemplate.registerListener(new DefaultListenerSupport());

// 执行结果
2023-06-11 13:17:58.228  INFO 15460 --- [           main] c.p.l.d.service.DefaultListenerSupport   : onOpen
throwRunEx executed, arg: newBee2, time: 1686460678228
2023-06-11 13:17:58.228  INFO 15460 --- [           main] c.p.l.d.service.DefaultListenerSupport   : onError
throwRunEx executed, arg: newBee2, time: 1686460678342
2023-06-11 13:17:58.342  INFO 15460 --- [           main] c.p.l.d.service.DefaultListenerSupport   : onError
2023-06-11 13:17:58.342  INFO 15460 --- [           main] c.p.l.d.service.DefaultListenerSupport   : onClose

java.lang.RuntimeException: this is a test runtime exception!

参考