在實際工作中嫂拴,重處理是一個非常常見的場景,比如:
- 發(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種重試策略:
- NeverRetryPolicy:只允許調(diào)用 RetryCallback 一次面粮,不允許重試
- AlwaysRetryPolicy:允許無限重試,直到成功继低,此方式邏輯不當會導(dǎo)致死循環(huán)
- SimpleRetryPolicy:固定次數(shù)重試策略但金,默認重試最大次數(shù)為 3 次,RetryTemplate 默認使用的策略
- TimeoutRetryPolicy:超時時間重試策略郁季,默認超時時間為 1 秒冷溃,在指定的超時時間內(nèi)允許重試
- CircuitBreakerRetryPolicy:有熔斷功能的重試策略,需設(shè)置 3 個參數(shù) openTimeout梦裂、resetTimeout 和 delegate
- 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)類里面
注意事項
- 由于是基于AOP實現(xiàn),所以不支持類里自調(diào)用方法(動態(tài)代理原因)
- 如果重試失敗需要給@Recover注解的方法做后續(xù)處理甜刻,那這個重試的方法不能有返回值绍撞,只能是void
- 方法內(nèi)不能使用try catch,只能往外拋異常
- @Recover注解來開啟重試失敗后調(diào)用的方法(注意,需跟重處理方法在同一個類中)罢吃,此注解注釋的方法參數(shù)一定要是@Retryable拋出的異常楚午,否則無法識別,可以在該方法中進行日志處理尿招。