@Retryable注解重試功能

在實際工作中嫂拴,重處理是一個非常常見的場景,比如:

  • 發(fā)送消息失敗贮喧。
  • 調(diào)用遠程服務(wù)失敗筒狠。
  • 爭搶鎖失敗。

這些錯誤可能是因為網(wǎng)絡(luò)波動造成的箱沦,等待過后重處理就能成功辩恼。通常來說,會用try/catch谓形,while循環(huán)之類的語法來進行重處理灶伊,但是這樣的做法缺乏統(tǒng)一性,并且不是很方便寒跳,要多寫很多代碼聘萨。然而spring-retry卻可以通過注解,在不入侵原有業(yè)務(wù)邏輯代碼的方式下童太,優(yōu)雅的實現(xiàn)重處理功能米辐。

源碼分析:扒一扒@Retryable注解,很優(yōu)雅书释,有點意思翘贮!

@Retryable

spring系列的spring-retry是另一個實用程序模塊,可以幫助我們以標準方式處理任何特定操作的重試征冷。在spring-retry中择膝,所有配置都是基于簡單注釋的。

使用步驟:

① POM依賴
<dependency>
  <groupId>org.springframework.retry</groupId>
  <artifactId>spring-retry</artifactId>
 </dependency>
 <!--由于該組件是依賴于 AOP 給你的检激,所以還需要引入這個依賴-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.6.3</version>
</dependency>
② 啟用
@EnableRetry
@SpringBootApplication
public class HelloApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
    }
}
③ 使用
@Service
@Slf4j
public class IRetryServiceImpl implements IRetryService {

    @Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 3600,multiplier = 1.5))
    @Override
    public Boolean test(Integer code) {
        log.info("current-time===============>"+ DateUtil.date());
        if(code<0)
        {
            throw new RuntimeException("數(shù)字不能那個小于0");
        }
        return Boolean.TRUE;
    }
}

來簡單解釋一下注解中幾個參數(shù)的含義:

  • value:拋出指定異常才會重試
  • include:和value一樣肴捉,默認為空,當exclude也為空時叔收,默認所有異常
  • exclude:指定不處理的異常
  • maxAttempts:最大重試次數(shù)齿穗,默認3次
  • backoff:重試等待策略,默認使用@Backoff饺律,@Backoff的value默認為1000L窃页,我們設(shè)置為2000L;multiplier(指定延遲倍數(shù))默認為0,表示固定暫停1秒后進行重試脖卖,如果把multiplier設(shè)置為1.5乒省,則第一次重試為2秒,第二次為3秒畦木,第三次為4.5秒袖扛。

@Retryable參數(shù)詳解

參數(shù) 含義
interceptor 重試攔截器bean名稱,用于可重試方法
value 可重試的異常類型十籍。含義同include蛆封。默認為空(如果excludes也為空,則重試所有異常)
include 可重試的異常類型勾栗。默認為空(如果excludes也為空惨篱,則重試所有異常)
exclude 無需重試的異常類型。默認為空(如果includes也為空围俘,則重試所有異常)
label 統(tǒng)計報告的唯一標簽砸讳。如果未提供,則調(diào)用者可以選擇忽略它或提供一個默認值楷拳。
stateful 若為true绣夺,標志重試是有狀態(tài)的:即重新拋出異常吏奸,但是重試策略與相同的策略應(yīng)用于具有相同參數(shù)的后續(xù)調(diào)用欢揖。若為false,則不會重新引發(fā)可重試的異常
maxAttempts 最大重試次數(shù)(包括第一次失敗)奋蔚,默認為3次
maxAttemptsExpression 計算最大嘗試次數(shù)(包括第一次失敗)的表達式她混,默認為3 次
backoff 重試等待策略,下面會在@Backoff中介紹
exceptionExpression 指定在SimpleRetryPolicy.canRetry()返回true之后要求值的表達式-可用于有條件地禁止重試。

@backoff參數(shù)詳解

參數(shù) 含義
value 重試間隔
delay 重試之間的等待時間(以毫秒為單位)
maxDelay 重試之間的最大等待時間(以毫秒為單位)
multiplier 指定延遲的倍數(shù)
delayExpression 重試之間的等待時間表達式
maxDelayExpression 重試之間的最大等待時間表達式
multiplierExpression 指定延遲的倍數(shù)表達式
random 隨機指定延遲時間

當重試耗盡時還是失敗泊碑,會出現(xiàn)什么情況呢坤按?

當重試耗盡時,RetryOperations可以將控制傳遞給另一個回調(diào)馒过,即RecoveryCallback臭脓。Spring-Retry還提供了@Recover注解,用于@Retryable重試失敗后處理方法腹忽。如果不需要回調(diào)方法来累,可以直接不寫回調(diào)方法,那么實現(xiàn)的效果是窘奏,重試次數(shù)完了后嘹锁,如果還是沒成功沒符合業(yè)務(wù)判斷,就拋出異常着裹。

除了使用注解來實現(xiàn)方法重試以外领猾,Spring還提供了RetryTemplate來顯式進行方法的重試,大致實現(xiàn)方式如下圖所示:


RetryTemplate

從圖中可以發(fā)現(xiàn),我們可以通過setRetryPolicy()方法來為RetryTemplate設(shè)置重試策略摔竿。Spring為我們提供了6種重試策略:

  1. NeverRetryPolicy:只允許調(diào)用 RetryCallback 一次面粮,不允許重試
  2. AlwaysRetryPolicy:允許無限重試,直到成功继低,此方式邏輯不當會導(dǎo)致死循環(huán)
  3. SimpleRetryPolicy:固定次數(shù)重試策略但金,默認重試最大次數(shù)為 3 次,RetryTemplate 默認使用的策略
  4. TimeoutRetryPolicy:超時時間重試策略郁季,默認超時時間為 1 秒冷溃,在指定的超時時間內(nèi)允許重試
  5. CircuitBreakerRetryPolicy:有熔斷功能的重試策略,需設(shè)置 3 個參數(shù) openTimeout梦裂、resetTimeout 和 delegate
  6. CompositeRetryPolicy:組合重試策略似枕。有兩種組合方式,樂觀組合重試策略是指只要有一個策略允許重試即可以年柠,悲觀組合重試策略是指只要有一個策略不允許重試即不可以凿歼。但不管哪種組合方式,組合中的每一個策略都會執(zhí)行
@Recover
@Service
@Slf4j
public class IRetryServiceImpl implements IRetryService {

    @Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 3600,multiplier = 1.5))
    @Override
    public Boolean test(Integer code) {
        log.info("current-time===============>"+ DateUtil.date());
        if(code<0)
        {
            throw new RuntimeException("數(shù)字不能那個小于0");
        }
        return Boolean.TRUE;
    }

    @Recover
    public Boolean recover(Exception e)
    {
        log.info("回調(diào)方法執(zhí)行H吆蕖4疸尽!掀抹!");
        log.info("{}"+e.getMessage());
       //記日志到數(shù)據(jù)庫 或者調(diào)用其余的方法
        return Boolean.FALSE;
    }
}

可以看到傳參里面寫的是 Exception e虐拓,這個是作為回調(diào)的接頭暗號(重試次數(shù)用完了,還是失敗傲武,拋出這個Exception e通知觸發(fā)這個回調(diào)方法)蓉驹。對于@Recover注解的方法,需要特別注意的是:

1. 方法的返回值必須與@Retryable方法一致
方法的第一個參數(shù)揪利,必須是Throwable類型的态兴,建議是與@Retryable配置的異常一致,其他的參數(shù)疟位,需要哪個參數(shù)瞻润,寫進去就可以了(@Recover方法中有的)
2. 該回調(diào)方法與重試方法寫在同一個實現(xiàn)類里面

注意事項
  1. 由于是基于AOP實現(xiàn),所以不支持類里自調(diào)用方法(動態(tài)代理原因)
  2. 如果重試失敗需要給@Recover注解的方法做后續(xù)處理甜刻,那這個重試的方法不能有返回值绍撞,只能是void
  3. 方法內(nèi)不能使用try catch,只能往外拋異常
  4. @Recover注解來開啟重試失敗后調(diào)用的方法(注意,需跟重處理方法在同一個類中)罢吃,此注解注釋的方法參數(shù)一定要是@Retryable拋出的異常楚午,否則無法識別,可以在該方法中進行日志處理尿招。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末矾柜,一起剝皮案震驚了整個濱河市阱驾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌怪蔑,老刑警劉巖里覆,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異缆瓣,居然都是意外死亡喧枷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門弓坞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來隧甚,“玉大人,你說我怎么就攤上這事渡冻∑莅猓” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵族吻,是天一觀的道長帽借。 經(jīng)常有香客問我,道長超歌,這世上最難降的妖魔是什么砍艾? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮巍举,結(jié)果婚禮上脆荷,老公的妹妹穿的比我還像新娘。我一直安慰自己禀综,他們只是感情好简烘,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布苔严。 她就那樣靜靜地躺著定枷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪届氢。 梳的紋絲不亂的頭發(fā)上欠窒,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音退子,去河邊找鬼岖妄。 笑死,一個胖子當著我的面吹牛寂祥,可吹牛的內(nèi)容都是我干的荐虐。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼丸凭,長吁一口氣:“原來是場噩夢啊……” “哼福扬!你這毒婦竟也來了腕铸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤铛碑,失蹤者是張志新(化名)和其女友劉穎狠裹,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體汽烦,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡涛菠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了撇吞。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俗冻。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖牍颈,靈堂內(nèi)的尸體忽然破棺而出言疗,到底是詐尸還是另有隱情,我是刑警寧澤颂砸,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布噪奄,位于F島的核電站,受9級特大地震影響人乓,放射性物質(zhì)發(fā)生泄漏勤篮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一色罚、第九天 我趴在偏房一處隱蔽的房頂上張望碰缔。 院中可真熱鬧,春花似錦戳护、人聲如沸金抡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梗肝。三九已至,卻和暖如春铺董,著一層夾襖步出監(jiān)牢的瞬間巫击,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工精续, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留坝锰,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓重付,卻偏偏與公主長得像顷级,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子确垫,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容