1. 程式人生 > >Spring-Retry 模擬支付寶非同步通知商戶

Spring-Retry 模擬支付寶非同步通知商戶

一、依賴匯入。

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

二、service層如下。

import com.hteamtech.callback.common.RetMessage;
import org.springframework.retry.RetryException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @描述
 * @建立人 樑XL
 * @建立時間 2018/12/13
 */
@Service
@EnableRetry
public class TestRetryService {

    private static final String RES_SUCCESS = "success";
    private static final long TIME_UNIT = 1000 * 60;
    private static final int MAX_ATTEMPTS = 5;

    @Retryable(value = {RetryException.class, RuntimeException.class},
            maxAttempts = MAX_ATTEMPTS,
            backoff = @Backoff(delay = TIME_UNIT, maxDelay = TIME_UNIT * 6, multiplier = 2))
    public void testRetry(RetMessage msg) {
        // 呼叫商戶的介面
        String result = showMsg();

        // 列印時間
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String format = simpleDateFormat.format(new Date());
        System.out.println("第" + msg.getTimes() + "次非同步回撥,時間為" + format + ",返回結果為" + result);

        // 丟擲重試的異常
        if (!RES_SUCCESS.equals(result)) {
            msg.setTimes(msg.getTimes() + 1);
            msg.setSuccess(false);
            throw new RetryException("retry failed");
        } else {
            msg.setSuccess(true);
        }

    }

    public String showMsg() {
        return "false";
    }

    @Recover
    public void recover(RemoteAccessException e) {
        logger.info(e.getMessage());
    }
}

三、執行列印結果。

第1次非同步回撥,時間為2018-12-14 14:44:35,返回結果為false
第2次非同步回撥,時間為2018-12-14 14:45:35,返回結果為false
第3次非同步回撥,時間為2018-12-14 14:47:35,返回結果為false
第4次非同步回撥,時間為2018-12-14 14:51:35,返回結果為false
第5次非同步回撥,時間為2018-12-14 14:57:35,返回結果為false

// 之後便丟擲ReTry異常

四、註解解析

@Retryable

  • 被註解的方法發生異常時會重試
    • value:指定發生的異常進行重試
    • include:和value一樣,預設空,當exclude也為空時,所有異常都重試
    • exclude:指定異常不重試,預設空,當include也為空時,所有異常都重試
    • maxAttemps:重試次數,預設3
    • backoff:重試補償機制,預設沒有

@Backoff

  • 延遲策略
    • delay:指定延遲後重試
    • multiplier:指定延遲的倍數,比如delay=5000l,multiplier=2時,第一次重試為5秒後,第二次為10秒,第三次為20秒

@Recover

  • 當重試到達指定次數時,被註解的方法將被回撥,可以在該方法中進行日誌處理。需要注意的是發生的異常和入參型別一致時才會回撥。

五、避坑指南。

  1. 使用了@Retryable的方法不能再本類內被呼叫,不然重試機制不會生效。也就是要標記為@Service,然後再其他類使用@Autowired 注入或者@Bean去例項化才能生效。
  2. 要觸發@Recover方法,那麼在@Retryable方法上不能有返回值,只能是void才能生效。
  3. 使用了@Retryable的方法裡面不能使用try…catch包裹,要丟擲異常,不然不會觸發。
  4. 在重試期間這個方法是同步的,如果使用類似Spring Cloud這種框架的熔斷機制時,可以結合重試機制來重試後返回結果。
  5. SpringRetry不只能注入方式去實現,還可以通過API的方式實現,類似熔斷處理的機制就基於API方式實現會比較寬鬆。