Guava?Retryer實(shí)現(xiàn)接口重試的示例
前言
小黑在開發(fā)中遇到個(gè)問題,我負(fù)責(zé)的模塊需要調(diào)用某個(gè)三方服務(wù)接口查詢信息,查詢結(jié)果直接影響后續(xù)業(yè)務(wù)邏輯的處理;
這個(gè)接口偶爾會(huì)因網(wǎng)絡(luò)問題出現(xiàn)超時(shí),導(dǎo)致我的業(yè)務(wù)邏輯無法繼續(xù)處理;
這個(gè)問題該如何解決呢?,小黑首先想到的就是重試嘛,如果失敗了就再調(diào)用一次。
問題來了,如果又失敗了呢?接著重試嘛。我們循環(huán)處理,比如循環(huán)5次,全失敗則任務(wù)服務(wù)不可用,結(jié)束調(diào)用。
如果我又想著5次調(diào)用間隔一段時(shí)間呢?第一次先隔1秒,然后3秒,然后5秒呢?
小黑發(fā)現(xiàn)事情沒那么簡單,如果自己搞容易出BUG呀。
轉(zhuǎn)念一想,這個(gè)常見挺常見,網(wǎng)上應(yīng)該有輪子呀,找找看。一不小心就讓我給找著啦,哈哈。
Guava Retryer
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 Retryer你可以自定義來執(zhí)行重試,同時(shí)也可以監(jiān)控每次重試的結(jié)果和行為,最重要的基于 Guava 風(fēng)格的重試方式真的很方便。
引入依賴
<dependency> <groupId>com.github.rholder</groupId> <artifactId>guava-retrying</artifactId> <version>2.0.0</version> </dependency>
快速開始
Callable<Boolean> callable = new Callable<Boolean>() { public Boolean call() throws Exception { return true; // do something useful here } }; Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder() .retryIfResult(Predicates.<Boolean>isNull()) // callable返回null時(shí)重試 .retryIfExceptionOfType(IOException.class) // callable拋出IOException重試 .retryIfRuntimeException() // callable拋出RuntimeException重試 .withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重試3次后停止 .build(); try { retryer.call(callable); } catch (RetryException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }
在Callable的call()方法返回null,拋出IOException或者RuntimeException時(shí)會(huì)重試;
將在嘗試重試3次后停止,并拋出包含上次失敗嘗試信息的RetryException;
如果call()方法中彈出任何其他異常,它將被包裝并在ExecutionException中重新調(diào)用。
指數(shù)退避(Exponential Backoff)
根據(jù)wiki上對(duì)Exponential backoff的說明,指數(shù)補(bǔ)償是一種通過反饋,成倍地降低某個(gè)過程的速率,以逐漸找到合適速率的算法。
在以太網(wǎng)中,該算法通常用于沖突后的調(diào)度重傳。根據(jù)時(shí)隙和重傳嘗試次數(shù)來決定延遲重傳。
在c次碰撞后(比如請(qǐng)求失?。瑫?huì)選擇0和2^c - 1之間的隨機(jī)值作為時(shí)隙的數(shù)量。
對(duì)于第1次碰撞來說,每個(gè)發(fā)送者將會(huì)等待0或1個(gè)時(shí)隙進(jìn)行發(fā)送。
而在第2次碰撞后,發(fā)送者將會(huì)等待0到3( 由2^2 -1 計(jì)算得到)個(gè)時(shí)隙進(jìn)行發(fā)送。
而在第3次碰撞后,發(fā)送者將會(huì)等待0到7( 由2^3 - 1 計(jì)算得到)個(gè)時(shí)隙進(jìn)行發(fā)送。
以此類推……
Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder() .retryIfExceptionOfType(IOException.class) .retryIfRuntimeException() .withWaitStrategy(WaitStrategies.exponentialWait(100, 5, TimeUnit.MINUTES)) // 指數(shù)退避 .withStopStrategy(StopStrategies.neverStop()) // 永遠(yuǎn)不停止重試 .build();
創(chuàng)建一個(gè)永遠(yuǎn)重試的重試器,在每次重試失敗后以指數(shù)級(jí)退避間隔遞增,直到最多5分鐘。5分鐘后,從那時(shí)起每隔5分鐘重試一次。
斐波那契退避(Fibonacci Backoff)
斐波那契數(shù)列指的是這樣一個(gè)數(shù)列:
0,1,1,2,3,5,8,13,21,34,55,89...
這個(gè)數(shù)列從第3項(xiàng)開始,每一項(xiàng)都等于前兩項(xiàng)之和。
Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder() .retryIfExceptionOfType(IOException.class) .retryIfRuntimeException() .withWaitStrategy(WaitStrategies.fibonacciWait(100, 2, TimeUnit.MINUTES)) // 斐波那契退避 .withStopStrategy(StopStrategies.neverStop()) .build();
創(chuàng)建一個(gè)永遠(yuǎn)重試的重試器,在每次重試失敗后以增加斐波那契退避間隔的方式等待,直到最多2分鐘。2分鐘后,從那時(shí)起每隔2分鐘重試一次。
與指數(shù)退避策略類似,斐波那契退避策略遵循一種模式,即在每次嘗試失敗后等待的時(shí)間越來越長。
對(duì)于這兩種策略的性能英國利茲大學(xué)專門做過性能測試,相比指數(shù)退避策略,斐波那契退避策略可能性能更好,吞吐量可能也更好。
重試監(jiān)聽器
當(dāng)重試發(fā)生時(shí),如果需要額外做一些動(dòng)作,比如發(fā)送郵件通知之類的,可以通過RetryListener,Guava Retryer在每次重試之后會(huì)自動(dòng)回調(diào)監(jiān)聽器,并且支持注冊(cè)多個(gè)監(jiān)聽。
@Slf4j class DiyRetryListener<Boolean> implements RetryListener { @Override public <Boolean> void onRetry(Attempt<Boolean> attempt) { log.info("重試次數(shù):{}",attempt.getAttemptNumber()); log.info("距離第一次重試的延遲:{}",attempt.getDelaySinceFirstAttempt()); if(attempt.hasException()){ log.error("異常原因:",attempt.getExceptionCause()); }else { System.out.println("正常處理結(jié)果:{}" + attempt.getResult()); } } }
定義監(jiān)聽器之后,需要在Retryer中進(jìn)行注冊(cè)。
Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder() .retryIfResult(Predicates.<Boolean>isNull()) // callable返回null時(shí)重試 .retryIfExceptionOfType(IOException.class) // callable拋出IOException重試 .retryIfRuntimeException() // callable拋出RuntimeException重試 .withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重試3次后停止 .withRetryListener(new DiyRetryListener<Boolean>()) // 注冊(cè)監(jiān)聽器 .build();
小結(jié)
Guava Retryer不光在重試策略上支持多種選擇,并且將業(yè)務(wù)邏輯的處理放在Callable中,和重試處理邏輯分開,實(shí)現(xiàn)了解耦,這比小黑自己去寫循環(huán)處理要優(yōu)秀太多啦,Guava確實(shí)強(qiáng)大。
到此這篇關(guān)于Guava Retryer實(shí)現(xiàn)接口重試的示例的文章就介紹到這了,更多相關(guān)Guava Retryer 口重試 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用poi-tl1.9.1生成Word文檔的技巧分享
本文將簡單介紹poi-tl的相關(guān)知識(shí),通過一個(gè)實(shí)際的案例實(shí)踐,充分介紹如何利用poi-tl進(jìn)行目標(biāo)文檔的生成,同時(shí)分享幾個(gè)不同的office版本如何進(jìn)行圖表生成的解決方案,需要的朋友可以參考下2023-09-09java,android,MD5加密算法的實(shí)現(xiàn)代碼(16位,32位)
下面小編就為大家?guī)硪黄猨ava,android,MD5加密算法的實(shí)現(xiàn)代碼(16位,32位)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-09-09Java基于命令模式實(shí)現(xiàn)郵局發(fā)信功能詳解
這篇文章主要介紹了Java基于命令模式實(shí)現(xiàn)郵局發(fā)信功能,較為詳細(xì)的分析了命令行模式的概念、原理并結(jié)合實(shí)例形式分析了Java使用命令行模式實(shí)現(xiàn)郵局發(fā)信功能的相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下2018-04-04mybatis利用association或collection傳遞多參數(shù)子查詢
今天小編就為大家分享一篇關(guān)于mybatis利用association或collection傳遞多參數(shù)子查詢,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03Java中關(guān)于MouseWheelListener的鼠標(biāo)滾輪事件詳解
這篇文章主要介紹了Java中關(guān)于MouseWheelListener的鼠標(biāo)滾輪事件詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12Spring Cloud中FeignClient實(shí)現(xiàn)文件上傳功能
這篇文章主要為大家詳細(xì)介紹了Spring Cloud中FeignClient實(shí)現(xiàn)文件上傳功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04