Spring中的接口重試機制解析
背景
大家在做項目的時候,往往會遇到一些接口由于網(wǎng)絡抖動等問題導致接口響應超時等,這時候我們會希望能夠按照一定的規(guī)則進行接口 請求重試。
分析
一般情況下,以上描述的情況,我們可能需要后臺的定時任務去重新發(fā)起調(diào)用,以達到目的,這樣無疑會增加開發(fā)成本,并且還得考慮 請求報文的保存等等問題。
這時我們可以使用Spring提供的功能來完成這個需求。
實現(xiàn)
假設我們現(xiàn)在有一個接口,在這個接口流程中會出現(xiàn)一些異常,比如超時異常(數(shù)據(jù)庫的異常、遠程調(diào)用的異常等),出現(xiàn)這樣的異常 我們就希望能夠自動發(fā)起重新調(diào)用的功能。
創(chuàng)建工程
我們?yōu)榱藴y試方便就直接創(chuàng)建一個簡單的Spring Boot的工程就可以了。創(chuàng)建的時候引入如下依賴:
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <!--AOP依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
說明:由于Spring重試機制的基于注解的AOP實現(xiàn),所以我們需要映入AOP的依賴,我們是Spring Boot項目直接使用AOP的啟動器就可以了。
啟動注解
由于我們是繼續(xù)Spring Boot來開發(fā)這部分代碼,所以我們需要配置開啟重試機制的的注解,代碼如下:
@SpringBootApplication @EnableRetry public class SpringbootApplication { public static void main(String[] args) { SpringApplication.run(SpringbootApplication.class, args); } }
說明:Spring重試機制主要是使用注解@Retryable來實現(xiàn)的
@Retryable
該注解的源碼如下:
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Retryable { String recover() default ""; String interceptor() default ""; Class<? extends Throwable>[] value() default {}; Class<? extends Throwable>[] include() default {}; Class<? extends Throwable>[] exclude() default {}; String label() default ""; boolean stateful() default false; int maxAttempts() default 3; String maxAttemptsExpression() default ""; Backoff backoff() default @Backoff; String exceptionExpression() default ""; String[] listeners() default {}; }
說明:
- value:拋出指定異常才會重試
- include:和value一樣,默認為空,當exclude也為空時,默認所有異常
- exclude:指定不處理的異常
- maxAttempts:最大重試次數(shù),默認3次
- backoff:重試等待策略, 默認使用@Backoff,@Backoff的value默認為1000, 以毫秒為單位的延遲(默認 1000)
- multiplier(指定延遲倍數(shù))默認為0,表示固定暫停1秒后進行重試,如果把multiplier設置為1.5,則第一次重試為2秒,第二次為3秒,第三次為4.5秒。
注解@Retryable切面類
該注解主要的切面攔截器如下代碼,感興趣的小伙伴可以自行查看源碼,分析。
AnnotationAwareRetryOperationsInterceptor#invoke()
@Recover
Spring-Retry還提供了@Recover注解,用于@Retryable重試失敗后處理方法。如果不需要回調(diào)方法,可以直接不寫回調(diào)方法,那么實現(xiàn)的效果是,重試次數(shù)完了后,如果還是沒成功沒符合業(yè)務判斷,就拋出異常。 可以看到傳參里面寫的是Exception e,這個是作為回調(diào)的標識(重試次數(shù)用完了,還是失敗,我們拋出這個Exception e通知觸發(fā)這個回調(diào)方法)。
注意事項:
1、方法的返回值必須與@Retryable方法一致
2、方法的第一個參數(shù),必須是Throwable類型的,建議是與@Retryable配置的異常一致,其他的參數(shù),需要哪個參數(shù),寫進去就可以了(@Recover方法中有的)
3、該回調(diào)方法與重試方法寫在同一個實現(xiàn)類里面
4、由于是基于AOP實現(xiàn),所以不支持類里自調(diào)用方法
5、如果重試失敗需要給@Recover注解的方法做后續(xù)處理,那這個重試的方法不能有返回值,只能是void
6、方法內(nèi)不能使用try catch,只能往外拋異常
7、@Recover注解來開啟重試失敗后調(diào)用的方法(注意,需跟重處理方法在同一個類中),此注解注釋的方法參數(shù)一定要是@Retryable拋出的異常。
該注解的代碼如下:
@Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Import(RetryConfiguration.class) @Documented public @interface Recover { }
注解@Recover切面類
AnnotationAwareRetryOperationsInterceptor#getRecoverer()
寫一個Demo 創(chuàng)建接口
首先我們創(chuàng)建一個接口。代碼如下:
public interface RetryService { /** * 重試方法 * @param str * @return * @throws Exception */ String retry(String str) throws Exception; /** * 回調(diào)方法 * @param e * @param str * @return */ String recover(Exception e,String str); }
接口實現(xiàn)
上述接口的實現(xiàn)的代碼如下:
@Service @Slf4j public class RetryServiceImpl implements RetryService { /** * 重試方法 * @param str * @return */ @Override @Retryable(value = Exception.class,maxAttempts = 5,backoff = @Backoff(delay = 2000,multiplier = 1.5)) public String retry(String str) throws Exception { log.info("Service 請求入?yún)椋簕}",str); log.info("進入測試方法,目前時間為:{}",new Date()); if ("succ".equals(str)){ return "succ"; }else { throw new Exception("異常了!"); } } /** * 重試次數(shù)完成后,回調(diào)的方法 * @param e * @param str * @return */ @Override @Recover public String recover(Exception e, String str) { log.info("異常出現(xiàn)后的回調(diào)操作,入?yún)椋簕},當前時間為:{}", str,LocalDate.now()); return null; } }
測試
我們使用postman進行測試
創(chuàng)建測試類
我們創(chuàng)建一個控制器來測試功能。代碼如下:
@RestController @Slf4j public class RetryController { @Resource RetryService retryService; @GetMapping("/re") public String retry(@RequestParam("str") String str) throws Exception{ log.info("Controller 請求入?yún)椋簕}",str); return retryService.retry(str); } }
說明:我們的入?yún)⑹且粋€字符串,若傳入的字符串非succ那么手動拋出異常,我們觀察日志,看是否框架自動發(fā)起了重試。
測試
我們啟動Sping Boot項目,并且進行測試,來看一下效果。
使用postman請求我們的login方法,我們觀察一下日志。請求地址localhost:8111/re,日志如下:
2023-07-17 21:05:19.488 INFO 2007 --- [nio-8111-exec-2] o.t.s.l.controller.RetryController : Controller 請求入?yún)椋?11
2023-07-17 21:05:19.516 INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl : Service 請求入?yún)椋?11
2023-07-17 21:05:19.516 INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl : 進入測試方法,目前時間為:Mon Jul 17 21:05:19 CST 2023
2023-07-17 21:05:21.518 INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl : Service 請求入?yún)椋?11
2023-07-17 21:05:21.519 INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl : 進入測試方法,目前時間為:Mon Jul 17 21:05:21 CST 2023
2023-07-17 21:05:24.522 INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl : Service 請求入?yún)椋?11
2023-07-17 21:05:24.523 INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl : 進入測試方法,目前時間為:Mon Jul 17 21:05:24 CST 2023
2023-07-17 21:05:29.024 INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl : Service 請求入?yún)椋?11
2023-07-17 21:05:29.025 INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl : 進入測試方法,目前時間為:Mon Jul 17 21:05:29 CST 2023
2023-07-17 21:05:35.779 INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl : Service 請求入?yún)椋?11
2023-07-17 21:05:35.779 INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl : 進入測試方法,目前時間為:Mon Jul 17 21:05:35 CST 2023
2023-07-17 21:05:35.790 INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl : 異常出現(xiàn)后的回調(diào)操作,入?yún)椋?11,當前時間為:2023-07-17
可以看到,出現(xiàn)異常后框架自動發(fā)起了重試,在重試次數(shù)使用完成后,回調(diào)了異常處理的方法。
到此這篇關于Spring中的接口重試機制解析的文章就介紹到這了,更多相關Spring接口重試內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java通過MyBatis框架對MySQL數(shù)據(jù)進行增刪查改的基本方法
MyBatis框架由Java的JDBC API進一步封裝而來,在操作數(shù)據(jù)庫方面效果拔群,接下來我們就一起來看看Java通過MyBatis框架對MySQL數(shù)據(jù)進行增刪查改的基本方法:2016-06-06java數(shù)據(jù)結(jié)構(gòu)-堆實現(xiàn)優(yōu)先隊列
通常都把隊列比喻成排隊買東西,大家都很守秩序,先排隊的人就先買東西。但是優(yōu)先隊列有所不同,它不遵循先進先出的規(guī)則,而是根據(jù)隊列中元素的優(yōu)先權(quán),優(yōu)先權(quán)最大的先被取出,這篇文章主要介紹了java數(shù)據(jù)結(jié)構(gòu)-堆實現(xiàn)優(yōu)先隊列,感興趣的朋友一起看看吧2021-08-08java Bean與json對象間的轉(zhuǎn)換實例講解
在本篇文章里小編給大家整理的是關于java Bean與json間的轉(zhuǎn)換的實例內(nèi)容,有需要的朋友們吧可以學習參考下。2020-01-01Spring?Cloud?Gateway?服務網(wǎng)關的部署與使用詳細講解
這篇文章主要介紹了Spring?Cloud?Gateway?服務網(wǎng)關的部署與使用詳細介紹,本文給大家講解的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-04-04Spring?createBeanInstance實例化Bean
這篇文章主要為大家介紹了Spring?createBeanInstance實例化Bean源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03