使用Guava retryer優雅的實現接口重調機制

  API 接口調用異常, 網絡異常在咱們平常開發中常常會遇到,這種狀況下咱們須要先重試幾回調用才能將其標識爲錯誤並在確認錯誤以後發送異常提醒。guava-retry能夠靈活的實現這一功能。Guava retryer在支持重試次數和重試頻度控制基礎上,可以兼容支持多個異常或者自定義實體對象的重試源定義,讓重試功能有更多的靈活性。Guava Retryer也是線程安全的,入口調用邏輯採用的是Java.util.concurrent.Callable的call方法。
java

  使用Guava retryer 很簡單,咱們只要作如下幾步:git

  Step一、引入Guava-retry  github

<guava-retry.version>2.0.0</guava-retry.version>
<dependency>
      <groupId>com.github.rholder</groupId>
      <artifactId>guava-retrying</artifactId>
      <version>${guava-retry.version}</version>
</dependency>

  Step二、定義實現Callable接口的方法,以便Guava retryer可以調用安全

 /**
     * @desc 更新可代理報銷人接口
     * @author jianzhang11
     * @date 2017/3/31 15:17
     */
    private static Callable<Boolean> updateReimAgentsCall = new Callable<Boolean>() {
        @Override
        public Boolean call() throws Exception {
            String url = ConfigureUtil.get(OaConstants.OA_REIM_AGENT);
            String result = HttpMethod.post(url, new ArrayList<BasicNameValuePair>());
            if(StringUtils.isEmpty(result)){
               throw new RemoteException("獲取OA可報銷代理人接口異常");
            }
            List<OAReimAgents> oaReimAgents = JSON.parseArray(result, OAReimAgents.class);
            if(CollectionUtils.isNotEmpty(oaReimAgents)){
                CacheUtil.put(Constants.REIM_AGENT_KEY,oaReimAgents);
                return true;
            }
            return false;
        }
    };

   Step三、定義Retry對象並設置相關策略網絡

Retryer<Boolean> retryer = RetryerBuilder
                .<Boolean>newBuilder()
                //拋出runtime異常、checked異常時都會重試,可是拋出error不會重試。
                .retryIfException()
                //返回false也須要重試
                .retryIfResult(Predicates.equalTo(false))
                //重調策略
                .withWaitStrategy(WaitStrategies.fixedWait(10, TimeUnit.SECONDS))
                //嘗試次數
                .withStopStrategy(StopStrategies.stopAfterAttempt(3))
                .build();

        try {
            retryer.call(updateReimAgentsCall);
        } catch (ExecutionException e) {
//            e.printStackTrace();
        } catch (RetryException e) {
            logger.error("更新可代理報銷人異常,須要發送提醒郵件");
        }

   簡單三步就能使用Guava Retryer優雅的實現重調方法。ide

   接下來對其進行詳細說明:  post

  RetryerBuilder是一個factory建立者,能夠定製設置重試源且能夠支持多個重試源,能夠配置重試次數或重試超時時間,以及能夠配置等待時間間隔,建立重試者Retryer實例。ui

  RetryerBuilder的重試源支持Exception異常對象 和自定義斷言對象,經過retryIfException 和retryIfResult設置,同時支持多個且能兼容。this

  retryIfException,拋出runtime異常、checked異常時都會重試,可是拋出error不會重試。url

  retryIfRuntimeException只會在拋runtime異常的時候才重試,checked異常和error都不重試。

  retryIfExceptionOfType容許咱們只在發生特定異常的時候才重試,好比NullPointerException和IllegalStateException都屬於runtime異常,也包括自定義的error

  如:  

 .retryIfExceptionOfType(Error.class)// 只在拋出error重試

  固然咱們還能夠在只有出現指定的異常的時候才重試,如:  

.retryIfExceptionOfType(IllegalStateException.class)  
.retryIfExceptionOfType(NullPointerException.class)  

  或者經過Predicate實現

.retryIfException(Predicates.or(Predicates.instanceOf(NullPointerException.class),  
                Predicates.instanceOf(IllegalStateException.class))) 

  retryIfResult能夠指定你的Callable方法在返回值的時候進行重試,如  

// 返回false重試 
.retryIfResult(Predicates.equalTo(false))  
//以_error結尾才重試 
.retryIfResult(Predicates.containsPattern("_error$"))  

  當發生重試以後,假如咱們須要作一些額外的處理動做,好比發個告警郵件啥的,那麼可使用RetryListener。每次重試以後,guava-retrying會自動回調咱們註冊的監聽。能夠註冊多個RetryListener,會按照註冊順序依次調用。

import com.github.rholder.retry.Attempt;  
import com.github.rholder.retry.RetryListener;  
  
import java.util.concurrent.ExecutionException;  
  
public class MyRetryListener<Boolean> implements RetryListener {  
  
    @Override  
    public <Boolean> void onRetry(Attempt<Boolean> attempt) {  
  
        // 第幾回重試,(注意:第一次重試實際上是第一次調用)  
        System.out.print("[retry]time=" + attempt.getAttemptNumber());  
  
        // 距離第一次重試的延遲  
        System.out.print(",delay=" + attempt.getDelaySinceFirstAttempt());  
  
        // 重試結果: 是異常終止, 仍是正常返回  
        System.out.print(",hasException=" + attempt.hasException());  
        System.out.print(",hasResult=" + attempt.hasResult());  
  
        // 是什麼緣由致使異常  
        if (attempt.hasException()) {  
            System.out.print(",causeBy=" + attempt.getExceptionCause().toString());  
        } else {  
            // 正常返回時的結果  
            System.out.print(",result=" + attempt.getResult());  
        }  
  
        // bad practice: 增長了額外的異常處理代碼  
        try {  
            Boolean result = attempt.get();  
            System.out.print(",rude get=" + result);  
        } catch (ExecutionException e) {  
            System.err.println("this attempt produce exception." + e.getCause().toString());  
        }  
  
        System.out.println();  
    }  
} 

  接下來在Retry對象中指定監聽:  

.withRetryListener(new MyRetryListener<>())  

  效果以下:

相關文章
相關標籤/搜索