Spring錯誤異常重試框架guava-retrying

官網:https://github.com/rholder/guava-retryinghtml

Maven:https://mvnrepository.com/artifact/com.github.rholder/guava-retryingjava

下面示例是基於Spring Boot的,可是均可以用於Spring項目。目前最新版是2.0.0。git

集成步驟:github

POM引入:web

        <!-- https://mvnrepository.com/artifact/com.github.rholder/guava-retrying -->
        <dependency>
            <groupId>com.github.rholder</groupId>
            <artifactId>guava-retrying</artifactId>
            <version>2.0.0</version>
        </dependency>

直接一個類裏面進行操做,基於匿名內部類實現。spring

package com.jsoft.springboottest.springboottest1.controller;

import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.github.rholder.retry.RetryException;
import com.github.rholder.retry.Retryer;
import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.github.rholder.retry.WaitStrategies;
import com.google.common.base.Predicates;

@RestController
public class TestController {
    
    private static final Logger logger = LoggerFactory.getLogger(TestController.class);
    
    @RequestMapping("/show")
    public String show(){        
        Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder() .retryIfResult(Predicates.<Boolean>isNull())// 設置自定義段元重試源 .retryIfExceptionOfType(Exception.class)// 設置異常重試源 .retryIfRuntimeException()// 設置異常重試源 .withStopStrategy(StopStrategies.stopAfterAttempt(5))// 設置重試5次,一樣能夠設置重試超時時間 .withWaitStrategy(WaitStrategies.fixedWait(5L, TimeUnit.SECONDS))// 設置每次重試間隔,5秒 .build();  try {
            retryer.call(new Callable<Boolean>() {
                int i = 0;

                @Override
                public Boolean call() throws Exception {
                    i++;
                    logger.info("第{}次執行!", i);
                    // do something
                    if (i<6) {// 模擬錯2次
                        logger.info("模擬執行失敗!");
                        throw new IOException("異常");
                    }
                    logger.info("模擬執行成功!");
                    return true;                   
                }
            });
        } catch (RetryException e) {
            logger.info("超太重試次數", e);
        } catch (ExecutionException e) {
            logger.info("重試框架異常", e);
        }
        
        return "Hello World";        
    }


}

示例工程:https://github.com/easonjim/5_java_example/tree/master/springboottest/springboottest5segmentfault

詳細介紹:springboot

使用場景網絡

在平常開發中,咱們常常會遇到須要調用外部服務和接口的場景。外部服務對於調用者來講通常都是不可靠的,尤爲是在網絡環境比較差的狀況下,網絡抖動很容易致使請求超時等異常狀況,這時候就須要使用失敗重試策略從新調用 API 接口來獲取。重試策略在服務治理方面也有很普遍的使用,經過定時檢測,來查看服務是否存活(Active)。app

Guava Retrying是一個靈活方便的重試組件,包含了多種的重試策略,並且擴展起來很是容易。

用做者的話來講:

This is a small extension to Google’s Guava library to allow for the creation of configurable retrying strategies for an arbitrary function call, such as something that talks to a remote service with flaky uptime.

使用Guava-retrying你能夠自定義來執行重試,同時也能夠監控每次重試的結果和行爲,最重要的基於 Guava 風格的重試方式真的很方便。

代碼示例

如下會簡單列出guava-retrying的使用方式:

若是拋出IOException則重試,若是返回結果爲null或者等於2則重試,固定等待時長爲300 ms,最多嘗試3次; 

Callable<Integer> task = new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        return 2;
    }
};

Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()
        .retryIfResult(Predicates.<Integer>isNull())
        .retryIfResult(Predicates.equalTo(2))
        .retryIfExceptionOfType(IOException.class)
        .withStopStrategy(StopStrategies.stopAfterAttempt(3))
        .withWaitStrategy(WaitStrategies.fixedWait(300, TimeUnit.MILLISECONDS))
        .build();
try {
    retryer.call(task);
} catch (ExecutionException e) {
    e.printStackTrace();
} catch (RetryException e) {
    e.printStackTrace();
}

出現異常則執行重試,每次任務執行最長執行時間限定爲 3 s,重試間隔時間初始爲 3 s,最多重試 1 分鐘,隨着重試次數的增長每次遞增 1 s,每次重試失敗,打印日誌;

@Override
public Integer call() throws Exception {
        return 2;
    }
};

Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()
        .retryIfException()
        .withStopStrategy(StopStrategies.stopAfterDelay(30,TimeUnit.SECONDS))
        .withWaitStrategy(WaitStrategies.incrementingWait(3, TimeUnit.SECONDS,1,TimeUnit.SECONDS))
        .withAttemptTimeLimiter(AttemptTimeLimiters.<Integer>fixedTimeLimit(3,TimeUnit.SECONDS))
        .withRetryListener(new RetryListener() {
            @Override
            public <V> void onRetry(Attempt<V> attempt) {
                if (attempt.hasException()){
                    attempt.getExceptionCause().printStackTrace();
                }
            }
        })
        .build();
try {
    retryer.call(task);
} catch (ExecutionException e) {
    e.printStackTrace();
} catch (RetryException e) {
    e.printStackTrace();
} 

核心執行邏輯分析:

long startTime = System.nanoTime();
for (int attemptNumber = 1; ; attemptNumber++) {
    Attempt<V> attempt;
    try {
        // 執行成功
        V result = attemptTimeLimiter.call(callable);
        attempt = new ResultAttempt<V>(result, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));
    } catch (Throwable t) {
        // 執行失敗
        attempt = new ExceptionAttempt<V>(t, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));
    }
    // 監聽器處理
    for (RetryListener listener : listeners) {
        listener.onRetry(attempt);
    }
    // 是否符合終止策略
    if (!rejectionPredicate.apply(attempt)) {
        return attempt.get();
    }
    // 是否符合中止策略
    if (stopStrategy.shouldStop(attempt)) {
        throw new RetryException(attemptNumber, attempt);
    } else {
        // 計算下次重試間隔時間
        long sleepTime = waitStrategy.computeSleepTime(attempt);
        try {
            blockStrategy.block(sleepTime);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RetryException(attemptNumber, attempt);
        }
    }
}

 依賴引入

<dependency>
      <groupId>com.github.rholder</groupId>
      <artifactId>guava-retrying</artifactId>
      <version>2.0.0</version>
</dependency>

主要接口及策略介紹:

Attempt:一次執行任務;

AttemptTimeLimiter:單次任務執行時間限制(若是單次任務執行超時,則終止執行當前任務);

BlockStrategies:任務阻塞策略(通俗的講就是當前任務執行完,下次任務還沒開始這段時間作什麼……),默認策略爲:BlockStrategies.THREAD_SLEEP_STRATEGY 也就是調用 Thread.sleep(sleepTime);

RetryException:重試異常;

RetryListener:自定義重試監聽器,能夠用於異步記錄錯誤日誌;

StopStrategy:中止重試策略,提供三種:

  • StopAfterDelayStrategy :設定一個最長容許的執行時間;好比設定最長執行10s,不管任務執行次數,只要重試的時候超出了最長時間,則任務終止,並返回重試異常RetryException
  • NeverStopStrategy :不中止,用於須要一直輪訓知道返回指望結果的狀況;
  • StopAfterAttemptStrategy :設定最大重試次數,若是超出最大重試次數則中止重試,並返回重試異常;

WaitStrategy:等待時長策略(控制時間間隔),返回結果爲下次執行時長:

  • FixedWaitStrategy:固定等待時長策略;
  • RandomWaitStrategy:隨機等待時長策略(能夠提供一個最小和最大時長,等待時長爲其區間隨機值)
  • IncrementingWaitStrategy:遞增等待時長策略(提供一個初始值和步長,等待時間隨重試次數增長而增長)
  • ExponentialWaitStrategy:指數等待時長策略;
  • FibonacciWaitStrategy :Fibonacci 等待時長策略;
  • ExceptionWaitStrategy :異常時長等待策略;
  • CompositeWaitStrategy :複合時長等待策略;

 

參考:

http://blog.csdn.net/aitangyong/article/details/53894997

http://www.javashuo.com/article/p-ggbrwvbr-da.html

http://blog.csdn.net/aitangyong/article/details/53886293

http://baijiahao.baidu.com/s?id=1575327487081031&wfr=spider&for=pc

http://www.cnblogs.com/jianzh5/p/6651799.html

http://lintrip.com/2016/05/27/guava-retry/(以上部份內容轉自此篇文章)

相關文章
相關標籤/搜索