重试机制(RetryTemplate)

强制: 统一采用Spring-Retry库来完成重试操作,不得 通过循环等方式来自实现

方案有如下两种(推荐采用注解方式,特殊场景可以通过 RetryTemplate 自行定制):

  1. 注解式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    @Slf4j
    @EnableRetry
    @Service
    public class RetryCallService {

    // 最大重试次数
    private static final int MAX_ATTEMPTS = 10;

    // 延迟策略
    private static final long DELAY = 1000 * 2L;
    private static final long MAX_DELAY = DELAY * 60;

    private int count = 0;

    private long start = 0;

    @Retryable(value = {BizException.class},
    maxAttempts = MAX_ATTEMPTS,
    backoff = @Backoff(delay = DELAY, maxDelay = MAX_DELAY, multiplier = 2))
    public Boolean retry() throws Exception {

    if (start == 0) {
    start = System.currentTimeMillis();
    }
    doSomething();
    return true;
    }

    // 达到最大重试次数后的补偿方案
    @Recover
    public Boolean recover(BizException e) {
    log.info("达到最大尝试次数,执行最后的补偿操作,异常信息为:{}", e.getMessage());
    return false;
    }

    // 具体的业务逻辑
    private void doSomething() throws Exception {
    count++;
    log.info("进行第 {} 次尝试,当前时间过去了:{} 秒", count, (System.currentTimeMillis() - start) / 1000);
    throw new BizException("模拟业务异常");
    }
    }
  1. 模板模式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    @Slf4j
    @Service
    public class RetryCallServiceTwo {

    // 最大重试次数
    private static final int MAX_ATTEMPTS = 10;

    // 延迟策略
    private static final long INITIAL_INTERVAL = TimeUnit.SECONDS.toMillis(2);
    private static final long MAX_INTERVAL = TimeUnit.MINUTES.toMillis(5);

    private int count = 0;

    private long start = 0;

    private RetryTemplate retryTemplate;

    // 初始化重试模板
    @PostConstruct
    public void initRetryTemplate() {
    retryTemplate = new RetryTemplate();
    ExponentialBackOffPolicy retryPolicy = new ExponentialBackOffPolicy();
    retryPolicy.setInitialInterval(INITIAL_INTERVAL);
    retryPolicy.setMaxInterval(MAX_INTERVAL);
    retryPolicy.setMultiplier(2);
    retryTemplate.setRetryPolicy(new SimpleRetryPolicy(MAX_ATTEMPTS));
    retryTemplate.setBackOffPolicy(retryPolicy);
    }

    public Boolean retry() throws Exception {

    // 典型的 Spring Template 用法
    retryTemplate.execute(new RetryCallback<Boolean, Exception>() {
    @Override
    public Boolean doWithRetry(RetryContext context) throws Exception {
    doSomething();
    return null;
    }
    }, new RecoveryCallback<Boolean>() {
    @Override
    public Boolean recover(RetryContext context) {
    return RetryCallServiceTwo.this.recover(context);
    }
    });
    return true;
    }

    // 达到最大重试次数后的补偿方案
    public Boolean recover(RetryContext retryContext) {
    log.info("达到最大尝试次数,执行最后的补偿操作,异常信息为:{}", retryContext.getLastThrowable().getMessage());
    return false;
    }

    private void doSomething() {
    if(start == 0) {
    start = System.currentTimeMillis();
    }
    count++;
    log.info("进行第 {} 次尝试,当前时间过去了:{} 秒", count, (System.currentTimeMillis() - start) / 1000);
    throw new BizException("模拟业务异常");
    }
    }