java-retry实现
有這樣一個(gè)需求,當(dāng)調(diào)用某個(gè)方法拋出異常,比如通過 HttpClient 調(diào)用遠(yuǎn)程接口時(shí)由于網(wǎng)絡(luò)原因報(bào) TimeOut 異常;或者所請(qǐng)求的接口返回類似于“處理中”這樣的信息,需要重復(fù)去查結(jié)果時(shí),我們希望當(dāng)前方法能夠在這種特定的情況下,重復(fù)執(zhí)行,如果達(dá)到了我們的期望,則不重復(fù)執(zhí)行。而且,我們希望能夠控制重試次數(shù),不希望無限期執(zhí)行下去。
Java 中有各種定時(shí)任務(wù)的實(shí)現(xiàn),如 Spring 的 Schedule,Quartz 等,稍微想一下,顯然不符合我們的需求。遞歸倒是可以,但是有些問題,先看下遞歸的實(shí)現(xiàn):
private int retryTimes = 3;@Testpublic void upperMethod() {method("123", "456");}public void method(String param1, String param2) {System.out.println(param1 + param2);// 其他一些操作,但是沒有得到預(yù)期的返回結(jié)果,或者拋出異常boolean isException = true;if(isException && retryTimes > 0){retryTimes --;method(param1, param2);}}method 方法是需要重復(fù)執(zhí)行的,重復(fù)執(zhí)行 3 次,加上第一次執(zhí)行,一共 4 次。如果異常了,則在 catch 里面遞歸調(diào)用 method。如果返回“處理中”等情況,則進(jìn)行判斷,是否需要遞歸調(diào)用。
這里的問題是定義了 retryTimes 這樣一個(gè)全局變量,不優(yōu)雅,如果需要重復(fù)執(zhí)行的方法較多,而且重復(fù)次數(shù)不一樣,則需定義多個(gè)全局變量。遞歸可以優(yōu)化一下:
@Testpublic void upperMethod() {method(3, "123", "456");}public void method(int retryTimes,String param1, String param2) {System.out.println(param1 + param2);// 其他一些操作,但是沒有得到預(yù)期的返回結(jié)果,或者拋出異常boolean isException = true;if(isException && retryTimes > 0){method(--retryTimes, param1, param2);}}這里去掉了全局變量,但是 method 方法多了一個(gè)和自身邏輯無關(guān)的 retryTimes 變量,還不優(yōu)雅。如果參數(shù)較多,還會(huì)顯得混亂。
下面做了一個(gè)還算優(yōu)雅的方法:
@Testpublic void mainMethod() {subMethod("123", "456");}public void subMethod(String param1, String param2) {System.out.println(param1 + param2);RetryUtil.setRetryTimes(3).retry(param1, param2);}增加了一個(gè) RetryUtil 的工具類,設(shè)置重試次數(shù),然后傳入當(dāng)前方法的參數(shù),進(jìn)行重復(fù)執(zhí)行。這里的重點(diǎn)就是 RetryUtil 的實(shí)現(xiàn):
public class RetryUtil {private static ThreadLocal<Integer> retryTimesInThread = new ThreadLocal<>();/*** 設(shè)置當(dāng)前方法重試次數(shù)** @param retryTimes* @return*/public static RetryUtil setRetryTimes(Integer retryTimes) {if (retryTimesInThread.get() == null)retryTimesInThread.set(retryTimes);return new RetryUtil();}/*** 重試當(dāng)前方法* <p>按順序傳入調(diào)用者方法的所有參數(shù)</p>** @param args* @return*/public Object retry(Object... args) {try {Integer retryTimes = retryTimesInThread.get();if (retryTimes <= 0) {retryTimesInThread.remove();return null;}retryTimesInThread.set(--retryTimes);String upperClassName = Thread.currentThread().getStackTrace()[2].getClassName();String upperMethodName = Thread.currentThread().getStackTrace()[2].getMethodName();Class clazz = Class.forName(upperClassName);Object targetObject = clazz.newInstance();Method targetMethod = null;for (Method method : clazz.getDeclaredMethods()) {if (method.getName().equals(upperMethodName)) {targetMethod = method;break;}}if (targetMethod == null)return null;targetMethod.setAccessible(true);return targetMethod.invoke(targetObject, args);} catch (Exception e) {e.printStackTrace();return null;}} }為了防止多線程情況下出現(xiàn)并發(fā)問題,這里定義了一個(gè) ThreadLocal 變量來存儲(chǔ)當(dāng)前線程的重試次數(shù)。然后通過 setRetryTimes ,一個(gè)靜態(tài)方法來設(shè)置這個(gè)重試次數(shù),并返回一個(gè) RetryUtil 對(duì)象。
調(diào)用者通過返回的 RetryUtil 對(duì)象調(diào)用 retry 方法實(shí)現(xiàn)重試。retry 方法接收一個(gè)可變參數(shù),因?yàn)檎{(diào)用者實(shí)際的參數(shù)不確定,這里要求按順序傳入調(diào)用者方法的所有參數(shù)。
接下來判斷 ThreadLocal 變量是否小于等于 0 ,如果是,則說明重復(fù)次數(shù)已達(dá)到,返回 null;如果不是,則讓 ThreadLocal 變量減一。接下來:
String upperClassName = Thread.currentThread().getStackTrace()[2].getClassName(); String upperMethodName = Thread.currentThread().getStackTrace()[2].getMethodName();來獲取當(dāng)前方法(retry)的上層方法名和上層類名。Thread.currentThread().getStackTrace() 得到線程的方法棧數(shù)組,數(shù)組的第二個(gè)元素 Thread.currentThread().getStackTrace()?[1]? 為當(dāng)前方法棧,第三個(gè)元素?Thread.currentThread().getStackTrace() [2] 為上層方法棧,通過上層方法的棧幀得到上層方法的方法名和類名。
下面就是通過反射獲取該類的所有方法,循環(huán)判斷方法名是否等于所要重復(fù)執(zhí)行的方法,如果是的話,執(zhí)行該方法,參數(shù)就是傳入可變參數(shù)。
可能大家會(huì)說反射會(huì)耗時(shí),但我認(rèn)為對(duì)于上述這種需求的情況,重試次數(shù)也不會(huì)太多,因此性能可以接受。
轉(zhuǎn)載于:https://www.cnblogs.com/itplay/p/10771861.html
與50位技術(shù)專家面對(duì)面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的java-retry实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: locate 和 find
- 下一篇: CNVD-C-2019-48814 漏