Spring?Retry優(yōu)雅地實(shí)現(xiàn)方法重試機(jī)制
前言
在實(shí)際的軟件開(kāi)發(fā)中,尤其是在涉及網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)庫(kù)操作或外部服務(wù)調(diào)用的場(chǎng)景下,我們常常會(huì)遇到一些臨時(shí)性故障(Transient Failures),例如網(wǎng)絡(luò)波動(dòng)、數(shù)據(jù)庫(kù)連接超時(shí)、第三方 API 暫時(shí)不可用等。面對(duì)這些問(wèn)題,一種常見(jiàn)的解決方案就是自動(dòng)重試機(jī)制。
Spring Retry 是 Spring 提供的一個(gè)模塊,它可以幫助我們以聲明式的方式為方法添加重試功能,從而提升系統(tǒng)的健壯性和可用性。
一、什么是 Spring Retry?
Spring Retry 是 Spring 框架中的一個(gè)子項(xiàng)目,提供了對(duì)方法調(diào)用失敗后進(jìn)行自動(dòng)重試的支持。它不僅支持簡(jiǎn)單的重試邏輯,還支持重試策略、回退策略以及與 Spring AOP 集成,使得我們可以非常方便地在業(yè)務(wù)代碼中加入重試邏輯。
二、Spring Retry 的核心組件
1. RetryTemplate
RetryTemplate 是 Spring Retry 的核心類(lèi)之一,它封裝了重試邏輯的執(zhí)行過(guò)程。你可以通過(guò)配置 RetryPolicy 和 BackOffPolicy 來(lái)定義重試次數(shù)和等待策略。
RetryTemplate retryTemplate = new RetryTemplate(); // 設(shè)置最多重試3次(包括第一次嘗試) retryTemplate.setRetryPolicy(new SimpleRetryPolicy(3)); // 設(shè)置固定間隔2秒再重試 retryTemplate.setBackOffPolicy(new FixedBackOffPolicy(2000L)); retryTemplate.execute(context -> { // 調(diào)用可能會(huì)失敗的方法 someService.doSomething(); return null; });
2. RetryPolicy
定義哪些異常需要重試,以及最大重試次數(shù)。常用的有:
- SimpleRetryPolicy:基于次數(shù)的重試。
- ExceptionClassifierRetryPolicy:根據(jù)異常類(lèi)型決定是否重試。
- NeverRetryPolicy:從不重試。
- AlwaysRetryPolicy:無(wú)限重試。
3. BackOffPolicy
定義重試之間的等待策略。常用的有:
- FixedBackOffPolicy:固定時(shí)間間隔。
- ExponentialBackOffPolicy:指數(shù)退避策略(推薦)。
- NoBackOffPolicy:不等待。
4. RetryListener
可以監(jiān)聽(tīng)重試的不同階段,比如開(kāi)始、重試、結(jié)束等事件,用于日志記錄或監(jiān)控。
三、使用注解方式實(shí)現(xiàn)重試(推薦)
Spring Retry 支持通過(guò)注解的方式簡(jiǎn)化重試邏輯的編寫(xiě),只需在方法上添加 @Retryable 注解即可。
1. 引入依賴(lài)(Maven)
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> <version>1.3.5</version> </dependency> <!-- 同時(shí)需要啟用 AspectJ --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency>
2. 啟用 Retry 功能
在配置類(lèi)或啟動(dòng)類(lèi)上加上 @EnableRetry 注解:
@SpringBootApplication @EnableRetry public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
3. 在方法上使用 @Retryable
@Service public class MyService { @Retryable(maxAttempts = 3, backoff = @Backoff(delay = 2000)) public void doSomething() { // 可能拋出異常的方法體 if (Math.random() < 0.7) { throw new RuntimeException("Temporary failure"); } System.out.println("Success!"); } }
上面的例子表示:
- 最多嘗試 3 次;
- 每次失敗后等待 2 秒;
- 默認(rèn)只對(duì) RuntimeException 進(jìn)行重試。
你也可以指定特定異常:
@Retryable(value = {IOException.class}, maxAttempts = 5)
或者排除某些異常:
@Retryable(exclude = {SQLException.class})
四、@Retryable 注解參數(shù)詳解
在使用 Spring Retry 的注解方式時(shí),@Retryable 是最核心的注解之一。它提供了多個(gè)可配置項(xiàng),用于定義方法的重試行為。下面是一個(gè)詳細(xì)的參數(shù)說(shuō)明表:
參數(shù)名 | 類(lèi)型 | 默認(rèn)值 | 描述 |
---|---|---|---|
value / include | Class<? extends Throwable>[] | {RuntimeException.class} | 指定需要重試的異常類(lèi)型,默認(rèn)對(duì)所有 RuntimeException 進(jìn)行重試。 |
exclude | Class<? extends Throwable>[] | {} | 排除某些異常類(lèi)型,這些異常不會(huì)觸發(fā)重試。 |
maxAttempts | int | 3 | 最大嘗試次數(shù)(包括第一次調(diào)用)。 |
maxAttemptsExpression | String | null | 支持通過(guò) SpEL 表達(dá)式動(dòng)態(tài)設(shè)置最大嘗試次數(shù)。 |
backoff | Backoff | @Backoff(delay = 1000L) | 設(shè)置退避策略,如固定延遲、指數(shù)退避等。 |
interceptorBeanName | String | "" | 自定義攔截器 Bean 名稱(chēng)(不常用)。 |
label | String | "" | 給重試操作添加標(biāo)簽,可用于監(jiān)聽(tīng)器識(shí)別。 |
stateful | boolean | false | 是否為有狀態(tài)重試(適用于冪等性要求高的場(chǎng)景)。 |
示例:更復(fù)雜的 @Retryable 配置
@Retryable( value = {IOException.class, TimeoutException.class}, exclude = SQLException.class, maxAttempts = 5, backoff = @Backoff(delay = 2000, multiplier = 1.5, maxDelay = 10000), stateful = true ) public void retryableMethod() { // 方法邏輯 }
在這個(gè)例子中:
- 只對(duì) IOException 和 TimeoutException 進(jìn)行重試;
- 排除 SQLException;
- 最多重試 5 次;
- 使用指數(shù)退避策略,初始延遲 2 秒,每次乘以 1.5 倍,最長(zhǎng)延遲不超過(guò) 10 秒;
- 啟用了有狀態(tài)重試(適合涉及外部狀態(tài)變更的操作);
五、高級(jí)用法:結(jié)合監(jiān)聽(tīng)器
你可以通過(guò)自定義監(jiān)聽(tīng)器來(lái)記錄每次重試的日志信息:
@Component public class MyRetryListener implements RetryListener { @Override public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) { System.out.println("Retry: Open"); return true; } @Override public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { System.out.println("Retry: Close"); } @Override public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { System.out.println("Retry: Error occurred, attempt " + context.getRetryCount()); } }
然后注冊(cè)到 RetryTemplate 中:
retryTemplate.registerListener(new MyRetryListener());
六、注意事項(xiàng)
冪等性問(wèn)題:重試操作必須是冪等的,否則重復(fù)執(zhí)行可能導(dǎo)致數(shù)據(jù)錯(cuò)誤。
事務(wù)控制:如果方法處于事務(wù)中,注意事務(wù)傳播行為,避免因重試引發(fā)事務(wù)沖突。
性能影響:合理設(shè)置重試次數(shù)和等待時(shí)間,防止系統(tǒng)負(fù)載過(guò)高。
異步 vs 同步:Spring Retry 是同步的,如需異步重試需自行結(jié)合線(xiàn)程池處理。
七、總結(jié)
Spring Retry 是一個(gè)輕量但功能強(qiáng)大的重試框架,它通過(guò)模板模式和注解方式幫助開(kāi)發(fā)者快速實(shí)現(xiàn)方法級(jí)別的自動(dòng)重試。無(wú)論是在微服務(wù)調(diào)用、消息消費(fèi)、數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)等場(chǎng)景中,都可以靈活應(yīng)用。
合理使用 Spring Retry,可以讓我們的系統(tǒng)更加健壯,有效應(yīng)對(duì)各種瞬時(shí)故障。
到此這篇關(guān)于Spring Retry優(yōu)雅地實(shí)現(xiàn)方法重試機(jī)制的文章就介紹到這了,更多相關(guān)Spring Retry方法重試內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java中@Configuration使用場(chǎng)景
本文主要介紹了java中@Configuration使用場(chǎng)景,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03使用Aop的方式實(shí)現(xiàn)自動(dòng)日志記錄的方式詳細(xì)介紹
這篇文章主要介紹了使用Aop的方式實(shí)現(xiàn)自動(dòng)日志記錄,通過(guò)監(jiān)聽(tīng)器去監(jiān)聽(tīng),當(dāng)訪(fǎng)問(wèn)到具體的類(lèi)方法,通過(guò)aop切面去獲取訪(fǎng)問(wèn)的方法,然后將日志記錄下來(lái),就這種方式給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-04-04解決SpringAop內(nèi)部調(diào)用時(shí)不經(jīng)過(guò)代理類(lèi)的問(wèn)題
這篇文章主要介紹了解決SpringAop內(nèi)部調(diào)用時(shí)不經(jīng)過(guò)代理類(lèi)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01JavaWeb之Ajax的基本使用與實(shí)戰(zhàn)案例
ajax技術(shù)是使頁(yè)面能局部刷新的一種技術(shù),下面這篇文章主要給大家介紹了關(guān)于JavaWeb之Ajax的基本使用與實(shí)戰(zhàn)案例的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08Java BufferWriter寫(xiě)文件寫(xiě)不進(jìn)去或缺失數(shù)據(jù)的解決
這篇文章主要介紹了Java BufferWriter寫(xiě)文件寫(xiě)不進(jìn)去或缺失數(shù)據(jù)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07Struts2學(xué)習(xí)教程之輸入校驗(yàn)示例詳解
這篇文章主要給大家介紹了關(guān)于Struts2學(xué)習(xí)教程之輸入校驗(yàn)的相關(guān)資料,文中通過(guò)示例介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用struts2具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-05-052019年最新Java學(xué)習(xí)路線(xiàn)圖
不管你是不懂電腦的小白,還是已經(jīng)步入開(kāi)發(fā)的大牛,這套路線(xiàn)路絕對(duì)不容錯(cuò)過(guò),路線(xiàn)圖的宗旨就是分享,專(zhuān)業(yè),便利,讓喜愛(ài)Java的人,都能平等的學(xué)習(xí),感興趣的同學(xué)可以了解一下2019-03-03