如何優雅地進行方法重試

程序調用第三方接口可能會出現網絡抖動、超時等異常狀況,這時咱們一般會想到當是重試。咱們首先模擬一段業務邏輯,而後開始咱們重試代碼當編寫java

/** * 這個是須要執行的業務邏輯 * 定義了一個隨機數,當低於閾值的時候,拋出異常 * 調用方catch住異常後進行重試 */
    private void doSomething() {
        log.info("開始業務邏輯...");
        int random = RandomUtils.nextInt(100);
        log.info("隨機數爲:{}", random);
        if (random < 90) {
            log.info("隨機數低於閾值,準備重試");
            throw new ServiceException("業務異常");
        }
        log.info("結束業務邏輯...");
    }
複製代碼

1. 普通重試

最普通的重試邏輯,就是在調用方捕獲到異常後,再次調用業務邏輯方法(遞歸),直到成功。該方案簡單粗暴spring

public void normal(int count) {
        if (count < 5) {
            count += 1;
            try {
                doSomething();
            } catch (Exception e) {
                //此處能夠定時休眠一點時間再次重試
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                //重試
                normal(count);
            }
        }
    }
複製代碼

2. SpringRetry 普通模式

Spring-retry是一個開源工具包,該工具把重試操做模板定製化,能夠設置重試策略和回退策略。同時重試執行實例保證線程安全,下面給出對應的示例代碼安全

/** * Spring提供的Retry機制 */
    public void springRetry() {
        // 構建重試模板實例
        RetryTemplate retryTemplate = new RetryTemplate();
        // 設置重試策略,主要設置重試次數和須要捕獲的異常
        SimpleRetryPolicy policy = new SimpleRetryPolicy(5, Collections.singletonMap(Exception.class, true));
        // 設置重試回退操做策略,主要設置重試間隔時間
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        //初始間隔
        backOffPolicy.setInitialInterval(1000);
        //最大間隔
        backOffPolicy.setMaxInterval(10 * 1000L);
        //遞增倍數(即下次間隔是上次的多少倍)
        backOffPolicy.setMultiplier(2);

        retryTemplate.setRetryPolicy(policy);
        retryTemplate.setBackOffPolicy(backOffPolicy);
        // 經過RetryCallback 重試回調實例包裝正常邏輯邏輯,第一次執行和重試執行執行的都是這段邏輯
        final RetryCallback<Object, Exception> retryCallback = context -> {
            log.info("開始重試,重試次數爲: {}", context.getRetryCount());
            doSomething();
            return null;
        };
        // 經過RecoveryCallback 重試流程正常結束或者達到重試上限後的退出恢復操做實例
        final RecoveryCallback<Object> recoveryCallback = context -> {
            log.info("執行重試結束依然失敗後的代碼");
            return null;
        };
        try {
            // 由retryTemplate 執行execute方法開始邏輯執行
            retryTemplate.execute(retryCallback, recoveryCallback);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
複製代碼

3. SpringRetry 註解版本

在上面的代碼基礎上,Spring提供了重試操做的註解,下面給出對應的示例代碼。使用該方式的時候須要注意如下兩點:bash

(1) 須要在入口類上添加@EnableRetry網絡

(2) @Retryable只能出如今最外層的方法,同一個類中,當某個方法調用加類該註解的方法時,重試不生效數據結構

/**
     * Spring提供的Retry機制
     */
    @Retryable(value = ServiceException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 2))
    public void springRetryAnnotation() {
        doSomething();
    }

    /**
     * 重試失敗後調用的方法(注意,需跟重處理方法在同一個類中)
     */
    @Recover
    public void recover(ServiceException e) {
        log.info("執行重試結束依然失敗後的代碼");
    }
複製代碼

4. GuavaRetry版本

SpringRetry版本只能對異常進行重試,對於自定義對數據結構不能支持,若是有這方面需求的化,能夠考慮用GuavaRetry進行重試,具體示例代碼以下(SpringTime默認不集成,使用時須要手動添加maven依賴)dom

/** * guava提供的retry機制 */
    public void guavaRetry() {
        //設置重試5次,一樣能夠設置重試超時時間
        StopStrategy stopStrategy = StopStrategies.stopAfterAttempt(5);
        //設置每次重試間隔
        WaitStrategy waitStrategy = WaitStrategies.exponentialWait(2, 10, TimeUnit.SECONDS);

        Retryer<Void> retryer = RetryerBuilder.<Void>newBuilder().retryIfException().withStopStrategy(stopStrategy)
                .withWaitStrategy(waitStrategy).build();
        try {
            retryer.call(() -> {
                doSomething();
                return null;
            });
        } catch (ExecutionException | RetryException e) {
            e.printStackTrace();
        }
    }
複製代碼
相關文章
相關標籤/搜索