Spring?Retry重試框架的使用講解
重試的使用場景比較多,比如調(diào)用遠(yuǎn)程服務(wù)時(shí),由于網(wǎng)絡(luò)或者服務(wù)端響應(yīng)慢導(dǎo)致調(diào)用超時(shí),此時(shí)可以多重試幾次。用定時(shí)任務(wù)也可以實(shí)現(xiàn)重試的效果,但比較麻煩,用Spring Retry的話一個(gè)注解搞定所有。話不多說,先看演示。
首先引入依賴
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> <version>1.3.4</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.9.1</version> </dependency>
使用方式有兩種:命令式和聲明式
命令式
/** * 命令式的方式使用Spring Retry */ @GetMapping("/hello") public String hello(@RequestParam("code") Integer code) throws Throwable { RetryTemplate retryTemplate = RetryTemplate.builder() .maxAttempts(3) .fixedBackoff(1000) .retryOn(RemoteAccessException.class) .build(); retryTemplate.registerListener(new MyRetryListener()); String resp = retryTemplate.execute(new RetryCallback<String, Throwable>() { @Override public String doWithRetry(RetryContext context) throws Throwable { return helloService.hello(code); } }); return resp; }
定義一個(gè)RetryTemplate,然后調(diào)用execute方法,可配置項(xiàng)比較多,不一一列舉
真正使用的時(shí)候RetryTemplate可以定義成一個(gè)Bean,例如:
@Configuration public class RetryConfig { @Bean public RetryTemplate retryTemplate() { RetryTemplate retryTemplate = RetryTemplate.builder() .maxAttempts(3) .fixedBackoff(1000) .withListener(new MyRetryListener()) .retryOn(RemoteAccessException.class) .build(); return retryTemplate; } }
業(yè)務(wù)代碼:
/** * 命令式的方式使用Spring Retry */ @Override public String hello(int code) { if (0 == code) { System.out.println("出錯(cuò)了"); throw new RemoteAccessException("出錯(cuò)了"); } System.out.println("處理完成"); return "ok"; }
監(jiān)聽器實(shí)現(xiàn):
package com.example.retry.listener; import org.springframework.retry.RetryCallback; import org.springframework.retry.RetryContext; import org.springframework.retry.RetryListener; public class MyRetryListener implements RetryListener { @Override public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) { System.out.println("open"); return true; } @Override public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { System.out.println("close"); } @Override public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { System.out.println("error"); } }
聲明式(注解方式)
/** * 注解的方式使用Spring Retry */ @Retryable(value = Exception.class, maxAttempts = 2, backoff = @Backoff(value = 1000, delay = 2000, multiplier = 0.5)) @Override public String hi(int code) { System.out.println("方法被調(diào)用"); int a = 1/code; return "ok"; } @Recover public String hiRecover(Exception ex, int code) { System.out.println("重試結(jié)束"); return "asdf"; }
這里需要主要的幾點(diǎn)
- @EnableRetry(proxyTargetClass = true/false)
- @Retryable 修飾的方法必須是public的,而且不能是同一個(gè)類中調(diào)用
- @Recover 修飾的方法簽名必須與@Retryable修飾的方法一樣,除了第一個(gè)參數(shù)外
/** * 注解的方式使用Spring Retry */ @GetMapping("/hi") public String hi(@RequestParam("code") Integer code) { return helloService.hi(code); }
1. 用法
聲明式
@Configuration @EnableRetry public class Application { } @Service class Service { @Retryable(RemoteAccessException.class) public void service() { // ... do something } @Recover public void recover(RemoteAccessException e) { // ... panic } }
命令式
RetryTemplate template = RetryTemplate.builder() .maxAttempts(3) .fixedBackoff(1000) .retryOn(RemoteAccessException.class) .build(); template.execute(ctx -> { // ... do something });
2. RetryTemplate
為了自動重試,Spring Retry 提供了 RetryOperations 重試操作策略
public interface RetryOperations { <T> T execute(RetryCallback<T> retryCallback) throws Exception; <T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback) throws Exception; <T> T execute(RetryCallback<T> retryCallback, RetryState retryState) throws Exception, ExhaustedRetryException; <T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback, RetryState retryState) throws Exception; }
基本回調(diào)是一個(gè)簡單的接口,允許插入一些要重試的業(yè)務(wù)邏輯:
public interface RetryCallback<T> { T doWithRetry(RetryContext context) throws Throwable; }
回調(diào)函數(shù)被嘗試,如果失?。ㄍㄟ^拋出異常),它將被重試,直到成功或?qū)崿F(xiàn)決定中止。
RetryOperations最簡單的通用實(shí)現(xiàn)是RetryTemplate
RetryTemplate template = new RetryTemplate(); TimeoutRetryPolicy policy = new TimeoutRetryPolicy(); policy.setTimeout(30000L); template.setRetryPolicy(policy); Foo result = template.execute(new RetryCallback<Foo>() { public Foo doWithRetry(RetryContext context) { // Do stuff that might fail, e.g. webservice operation return result; } });
從Spring Retry 1.3開始,RetryTemplate支持流式配置:
RetryTemplate.builder() .maxAttempts(10) .exponentialBackoff(100, 2, 10000) .retryOn(IOException.class) .traversingCauses() .build(); RetryTemplate.builder() .fixedBackoff(10) .withinMillis(3000) .build(); RetryTemplate.builder() .infiniteRetry() .retryOn(IOException.class) .uniformRandomBackoff(1000, 3000) .build();
3. RecoveryCallback
當(dāng)重試耗盡時(shí),RetryOperations可以將控制傳遞給不同的回調(diào):RecoveryCallback。
Foo foo = template.execute(new RetryCallback<Foo>() { public Foo doWithRetry(RetryContext context) { // business logic here }, new RecoveryCallback<Foo>() { Foo recover(RetryContext context) throws Exception { // recover logic here } });
4. Listeners
public interface RetryListener { void open(RetryContext context, RetryCallback<T> callback); void onSuccess(RetryContext context, T result); void onError(RetryContext context, RetryCallback<T> callback, Throwable e); void close(RetryContext context, RetryCallback<T> callback, Throwable e); }
在最簡單的情況下,open和close回調(diào)在整個(gè)重試之前和之后,onSuccess和onError應(yīng)用于個(gè)別的RetryCallback調(diào)用,onSuccess方法在成功調(diào)用回調(diào)之后被調(diào)用。
5. 聲明式重試
有時(shí),你希望在每次業(yè)務(wù)處理發(fā)生時(shí)都重試一些業(yè)務(wù)處理。這方面的典型例子是遠(yuǎn)程服務(wù)調(diào)用。Spring Retry提供了一個(gè)AOP攔截器,它將方法調(diào)用封裝在RetryOperations實(shí)例中。RetryOperationsInterceptor執(zhí)行被攔截的方法,并根據(jù)提供的RepeatTemplate中的RetryPolicy在失敗時(shí)重試。
你可以在 @Configuration 類上添加一個(gè) @EnableRetry 注解,并且在你想要進(jìn)行重試的方法(或者類)上添加 @Retryable 注解,還可以指定任意數(shù)量的重試監(jiān)聽器。
@Configuration @EnableRetry public class Application { @Bean public Service service() { return new Service(); } @Bean public RetryListener retryListener1() { return new RetryListener() {...} } @Bean public RetryListener retryListener2() { return new RetryListener() {...} } } @Service class Service { @Retryable(RemoteAccessException.class) public service() { // ... do something } }
可以使用 @Retryable 的屬性類控制 RetryPolicy 和 BackoffPolicy
@Service class Service { @Retryable(maxAttempts=12, backoff=@Backoff(delay=100, maxDelay=500)) public service() { // ... do something } }
如果希望在重試用盡時(shí)采用替代代碼返回,則可以提供恢復(fù)方法。方法應(yīng)該聲明在與@Retryable實(shí)例相同的類中,并標(biāo)記為@Recover。返回類型必須匹配@Retryable方法?;謴?fù)方法的參數(shù)可以包括拋出的異常和(可選地)傳遞給原始可重試方法的參數(shù)(或者它們的部分列表,只要在需要的最后一個(gè)之前不省略任何參數(shù))。
@Service class Service { @Retryable(RemoteAccessException.class) public void service(String str1, String str2) { // ... do something } @Recover public void recover(RemoteAccessException e, String str1, String str2) { // ... error handling making use of original args if required } }
若要解決可選擇用于恢復(fù)的多個(gè)方法之間的沖突,可以顯式指定恢復(fù)方法名稱。
@Service class Service { @Retryable(recover = "service1Recover", value = RemoteAccessException.class) public void service1(String str1, String str2) { // ... do something } @Retryable(recover = "service2Recover", value = RemoteAccessException.class) public void service2(String str1, String str2) { // ... do something } @Recover public void service1Recover(RemoteAccessException e, String str1, String str2) { // ... error handling making use of original args if required } @Recover public void service2Recover(RemoteAccessException e, String str1, String str2) { // ... error handling making use of original args if required } }
https://github.com/spring-projects/spring-retry
以上就是Spring Retry重試框架的使用講解的詳細(xì)內(nèi)容,更多關(guān)于Spring Retry重試的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java如何實(shí)現(xiàn)實(shí)體類轉(zhuǎn)Map、Map轉(zhuǎn)實(shí)體類
這篇文章主要介紹了Java 實(shí)現(xiàn)實(shí)體類轉(zhuǎn)Map、Map轉(zhuǎn)實(shí)體類的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08Mybatis 插入一條或批量插入 返回帶有自增長主鍵記錄的實(shí)例
下面小編就為大家分享一篇Mybatis 插入一條或批量插入 返回帶有自增長主鍵記錄的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12Java實(shí)現(xiàn)一個(gè)簡單的定時(shí)器代碼解析
這篇文章主要介紹了Java實(shí)現(xiàn)一個(gè)簡單的定時(shí)器代碼解析,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12java實(shí)現(xiàn)航空用戶管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)航空用戶管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07RocketMQ整合SpringBoot實(shí)現(xiàn)生產(chǎn)級二次封裝
本文主要介紹了RocketMQ整合SpringBoot實(shí)現(xiàn)生產(chǎn)級二次封裝,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06