
dependency groupIdorg.springframework.retry/groupId artifactIdspring-retry/artifactId /dependency前言在分布式系統(tǒng)、微服務(wù)架構(gòu)以及對(duì)外部服務(wù)如數(shù)據(jù)庫(kù)、第三方API、消息隊(duì)列的調(diào)用中網(wǎng)絡(luò)抖動(dòng)、服務(wù)瞬時(shí)不可用、資源競(jìng)爭(zhēng)等導(dǎo)致的臨時(shí)性失敗是常見(jiàn)現(xiàn)象。簡(jiǎn)單的一次性失敗重試可能引發(fā)“驚群效應(yīng)”而完全放棄重試則會(huì)降低系統(tǒng)可用性。Spring Retry 正是為解決這類問(wèn)題而生的聲明式重試框架它允許開(kāi)發(fā)者以注解或編程方式對(duì)可能失敗的操作配置靈活的重試策略、退避機(jī)制和兜底恢復(fù)邏輯從而提升系統(tǒng)的健壯性和容錯(cuò)能力。本文將詳細(xì)介紹 Spring Retry 的核心概念、注解用法與編程式 API幫助讀者在項(xiàng)目中快速引入并正確配置重試邏輯避免因不當(dāng)使用導(dǎo)致的資源耗盡、雪崩等問(wèn)題。一、概念spring對(duì)于重試機(jī)制的實(shí)現(xiàn)給了幾個(gè)抽象。BackOff補(bǔ)償值一般指失敗后多久進(jìn)行重試的延遲值。Sleeper暫停應(yīng)用的工具通常用來(lái)應(yīng)用補(bǔ)償值。BackOffPolicy補(bǔ)償策略決定失敗后如何確定補(bǔ)償值。RetryContext重試上下文代表了能被重試動(dòng)作使用的資源。RetryPolicy重試策略決定失敗能否重試。RecoveryCallback定義一個(gè)動(dòng)作recover在重試耗盡后的動(dòng)作。RetryCallback具體的重試動(dòng)作。RetryOperations通過(guò)傳遞RetryCallback進(jìn)行重試操作。RetryState重試狀態(tài)通常包含一個(gè)重試的鍵值。RetryStatistics和RetryListener用來(lái)監(jiān)控Retry的執(zhí)行情況并生成統(tǒng)計(jì)信息。github地址GitHub - spring-projects/spring-retryConfiguration EnableRetry public class Application { Bean public Service service() { return new Service(); } } Service class Service { Retryable(RemoteAccessException.class) public void service() { // ... do something } //spring-retry Recover不執(zhí)行2個(gè)方法返回值一致就ok了 //返回值和Retryable的返回值一致入?yún)⒑蛼伋霎惓R恢?Recover public void recover(RemoteAccessException e) { // ... panic } }通過(guò)EnableRetry就可以啟用Retry功能了需要被重試的方法加上Retryable(),就能在指定的異常出現(xiàn)情況下重試而當(dāng)默認(rèn)的失敗次數(shù)到達(dá)后查看SimpleRetryPolicy可知就是試3次就會(huì)調(diào)用Recover注解的方法進(jìn)行恢復(fù)。當(dāng)然在Retryable上可以配置屬性更加細(xì)化例如Retryable(value {RemoteAccessException.class},maxAttempts 5,backoff Backoff(delay 5000l,multiplier 1))指定重試5次每次補(bǔ)償(延遲5秒)每次倍數(shù)為1不變。幾個(gè)注解的參數(shù)解釋EnableRetry能否重試。當(dāng)proxyTargetClass屬性為true時(shí)使用CGLIB代理。默認(rèn)使用標(biāo)準(zhǔn)JAVA注解。在spring Boot中此參數(shù)寫在程序入口即可。Retryable 標(biāo)注此注解的方法在發(fā)生異常時(shí)會(huì)進(jìn)行重試value指定處理的異常類include指定處理的異常類和value一樣默認(rèn)為空當(dāng)exclude也為空時(shí)默認(rèn)所有異常exclude指定異常不處理默認(rèn)空當(dāng)include也為空時(shí)默認(rèn)所有異常maxAttempts最大重試次數(shù)。默認(rèn)3次-backoff 重試補(bǔ)償策略。默認(rèn)使用Backoff注解Backoff 重試補(bǔ)償策略不設(shè)置參數(shù)時(shí)默認(rèn)使用FixedBackOffPolicy指定等待時(shí)間重試等待1000ms設(shè)置delay,使用FixedBackOffPolicy指定等待- - 設(shè)置delay和maxDealy時(shí)重試等待在這兩個(gè)值之間均態(tài)分布設(shè)置delay、maxDealy、multiplier使用 ExponentialBackOffPolicy指數(shù)級(jí)重試間隔的實(shí)現(xiàn) multiplier即指定延遲倍數(shù)比如delay5000l,multiplier2,則第一次重試為5秒第二次為10秒第三次為20秒Recover 用于Retryable重試失敗后處理方法此注解注釋的方法參數(shù)一定要是Retryable拋出的異常否則無(wú)法識(shí)別可以在該方法中進(jìn)行日志處理。三、核心API-RetryTemplate聲明式的使用實(shí)際上是由spring-retry在內(nèi)部生成了一個(gè)默認(rèn)的RetryTemplate由它封裝我們自己寫的函數(shù)完成的重試。這有點(diǎn)像Scheduled內(nèi)部生成了一個(gè)ThreadPoolTaskExecutor完成定時(shí)任務(wù)的調(diào)度。雖然簡(jiǎn)單但是可配置的東西太少了如果想用spring-retry強(qiáng)大的策略機(jī)制并必須定制化RetryTemplate。官方的API使用demo如下RetryTemplate template new RetryTemplate(); TimeoutRetryPolicy policy new TimeoutRetryPolicy(); policy.setTimeout(30000L); template.setRetryPolicy(policy); Foo result template.execute(new RetryCallbackFoo() { public Foo doWithRetry(RetryContext context) { // Do stuff that might fail, e.g. webservice operation return result; } });可以看出new出一個(gè)RetryTemplate對(duì)象后可以給它設(shè)置重試策略、補(bǔ)償策略、重試監(jiān)聽(tīng)器等屬性。核心是在template.execute(),傳遞一個(gè)RetryCallback內(nèi)部執(zhí)行我們需要重試的具體方法。RetryTemplate是標(biāo)準(zhǔn)spring的××Template風(fēng)格腦補(bǔ)jdbcTemplate內(nèi)部doExecute()方法實(shí)現(xiàn)了如何開(kāi)啟重試上下文獲取補(bǔ)償上下文在try/catch中執(zhí)行doWithRetry(),出現(xiàn)異常捕捉下來(lái)如何應(yīng)用重試策略決定重試最后如何應(yīng)用回退方法關(guān)閉上下文等。這些都模板化了我們只需要要傳入RetryCallback和RecoveryCallback(連這個(gè)也可省)??偨Y(jié)與注意事項(xiàng)Spring Retry 為處理臨時(shí)性失敗提供了優(yōu)雅的解決方案但在使用時(shí)需要注意以下幾點(diǎn)使用要點(diǎn)明確重試場(chǎng)景僅對(duì)非冪等操作或非業(yè)務(wù)邏輯錯(cuò)誤如網(wǎng)絡(luò)超時(shí)、數(shù)據(jù)庫(kù)連接中斷啟用重試。對(duì)于參數(shù)錯(cuò)誤、權(quán)限不足等業(yè)務(wù)異常重試通常無(wú)效。合理配置重試策略根據(jù)被調(diào)用服務(wù)的 SLA 和自身系統(tǒng)容忍度設(shè)置合適的最大重試次數(shù)maxAttempts和退避策略Backoff避免過(guò)度重試拖垮系統(tǒng)。結(jié)合斷路器模式在微服務(wù)架構(gòu)中建議將 Spring Retry 與 Resilience4j 或 Hystrix 等斷路器框架結(jié)合使用。重試解決瞬時(shí)故障斷路器在服務(wù)持續(xù)不可用時(shí)快速失敗防止級(jí)聯(lián)故障。做好日志與監(jiān)控通過(guò)RetryListener或自定義切面記錄重試事件便于問(wèn)題排查和系統(tǒng)健康度評(píng)估。常見(jiàn)坑點(diǎn)代理機(jī)制限制Retryable基于 AOP 代理實(shí)現(xiàn)因此自調(diào)用同一個(gè)類中方法 A 調(diào)用方法 B且 B 有Retryable會(huì)失效。需要通過(guò)注入代理對(duì)象或使用AopContext.currentProxy()解決。異常類型匹配Recover方法的異常參數(shù)必須與Retryable方法拋出的異常類型嚴(yán)格匹配或?yàn)槠涓割惽曳祷刂殿愋托枰恢路駝t恢復(fù)方法不會(huì)被調(diào)用。上下文狀態(tài)清理在編程式使用RetryTemplate時(shí)注意RetryContext的生命周期避免在多線程環(huán)境下上下文狀態(tài)污染。資源泄漏風(fēng)險(xiǎn)長(zhǎng)時(shí)間的重試等待尤其是指數(shù)退避可能占用連接、線程等資源。務(wù)必設(shè)置超時(shí)TimeoutRetryPolicy或使用異步重試如結(jié)合Async。數(shù)據(jù)庫(kù)事務(wù)與冪等性在重試包含數(shù)據(jù)庫(kù)寫操作的業(yè)務(wù)時(shí)需考慮事務(wù)邊界和冪等性設(shè)計(jì)防止重復(fù)提交導(dǎo)致數(shù)據(jù)不一致??傊甋pring Retry 是一把利器但需在理解其原理和限制的基礎(chǔ)上謹(jǐn)慎使用方能真正提升系統(tǒng)的彈性。