spring Retryable注解實(shí)現(xiàn)重試詳解
spring-boot:1.5.3.RELEASE,spring-retry-1.2.0.RELEASE
使用方法
引入pom
// 版本號(hào)繼承spring-boot依賴管理的pom <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency>
啟用重試
@Configuration
@ImportResource(locations = { "classpath*:spring/app-context-*" })
@EnableRetry
public class AppContext {
}
注解需要重試的方法
@Retryable(value = RuntimeException.class, maxAttempts = 3,backoff = @Backoff(delay = 10L, multiplier = 1))
public boolean myRetryableMethod(){
...
}
注解屬性含義
Retryable
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Retryable {
/**
* 為重試方法應(yīng)用重試攔截器的bean名稱。與其他屬性互斥
*/
String interceptor() default "";
/**
* 可以重試的異常類型。與includes屬性同義。默認(rèn)值為空(并且如果exclude也是空的話,
* 所有的異常都會(huì)重試)
*/
Class<? extends Throwable>[] value() default {};
/**
* 同上
*/
Class<? extends Throwable>[] include() default {};
/**
* 與include含義相反
*/
Class<? extends Throwable>[] exclude() default {};
/**
* 統(tǒng)計(jì)報(bào)告的唯一標(biāo)簽。如果沒有提供,調(diào)用者可以選擇忽略它,或者提供一個(gè)默認(rèn)值。
*
* @return the label for the statistics
*/
String label() default "";
/**
* 標(biāo)識(shí)重試有狀態(tài)的:即異常重新拋出,但是重試策略使用相同的策略應(yīng)用于后續(xù)的具有相同參數(shù)的
* 調(diào)用。如果為false那么可重試的異常不會(huì)重新拋出。
*/
boolean stateful() default false;
/**
* 嘗試的最大次數(shù)(包含第一次失敗),默認(rèn)為3
*/
int maxAttempts() default 3;
/**
* 返回一個(gè)求嘗試最大次數(shù)值的表達(dá)式(包含第一次失敗),默認(rèn)為3
* 重寫 {@link #maxAttempts()}。
* @since 1.2
*/
String maxAttemptsExpression() default "";
/**
* 為正重試的動(dòng)作指定backoff屬性。默認(rèn)沒有backoff,但是在兩次嘗試之間暫定一下是一個(gè)很好的想法
* (即使代價(jià)是阻塞線程)
*/
Backoff backoff() default @Backoff();
/**
* 在{@code SimpleRetryPolicy.canRetry()}返回true之后指定一個(gè)計(jì)算表達(dá)式 - 可用來有條件的取消重試。
* 僅在調(diào)用拋出一個(gè)異常后。求值的root對(duì)象為上一次的異常 {@code Throwable}。
* 可以引用上下文中的其他beans。
* 例如:
* {@code "message.contains('you can retry this')"}.
* and
* {@code "@someBean.shouldRetry(#root)"}.
* @since 1.2
*/
String exceptionExpression() default "";
}
Backoff
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(RetryConfiguration.class)
@Documented
public @interface Backoff {
/**
* 與 {@link #delay()} 屬性同義
*
* 返回延遲多少毫秒后重試(默認(rèn)為1000毫秒)
*/
long value() default 1000;
/**
* 一個(gè)標(biāo)準(zhǔn)的再重試周期。在指數(shù)函數(shù)情況下用作初始值,在始終如一的情況下(固定周期值情況)
* 用作最小值。
* @return the initial or canonical backoff period in milliseconds (default 1000)???
*/
long delay() default 0;
/**
* 重試之間最大等待(毫秒)時(shí)間。如果小于 {@link #delay()} 則忽略。
* @return the maximum delay between retries (default 0 = ignored)
*/
long maxDelay() default 0;
/**
* 如果是正數(shù),則用于生成下次再重試等待時(shí)間的乘數(shù)。
* 返回一個(gè)乘數(shù)用于計(jì)算下次再重試延遲(默認(rèn)為0忽略)
*/
double multiplier() default 0;
/**
* 標(biāo)準(zhǔn)再重試周期求值表達(dá)式。在指數(shù)情況下用作初始值,始終如一的情況下用作最小值。
* 重寫 {@link #delay()}.
* @since 1.2
*/
String delayExpression() default "";
/**
* 在重試之間最大等待(毫秒)數(shù)的求值表達(dá)式。
* 如果小于 {@link #delay()} 則忽略。
* 重寫 {@link #maxDelay()}
* 默認(rèn)為0,忽略
* @since 1.2
*/
String maxDelayExpression() default "";
/**
* 表達(dá)式求值作為生成下次再重試延遲的乘數(shù)
* 重寫 {@link #multiplier()}。
* @since 1.2
*/
String multiplierExpression() default "";
/**
* 在指數(shù)情況下 ({@link #multiplier()} > 0) 設(shè)置該值為true將使再重試延遲隨機(jī)化,
* 使最大延遲為先前延遲的乘數(shù)倍數(shù),并使這兩個(gè)延遲值之間分布均勻。
* 默認(rèn)為false
*/
boolean random() default false;
}
案例
默認(rèn)retry
@Component
public class MyTask {
@Retryable
public void doExecute(){
System.out.println("## current Date:" + new Date());
throw new RuntimeException("my test");
}
}
輸出結(jié)果
## current Date:Sat Aug 29 21:54:55 CST 2020 ## current Date:Sat Aug 29 21:54:56 CST 2020 ## current Date:Sat Aug 29 21:54:57 CST 2020 2020-08-29 21:55:00,319 INFO [main] com.dianwoba.common.datasource.DataSourceAspect:invoke:32 restore database connection Exception in thread "main" java.lang.RuntimeException: my test ...
stateful
源碼相同,注解增加屬性配置
@Retryable( stateful = true )
public void doExecute(){
輸出結(jié)果
## current Date:Sat Aug 29 21:58:56 CST 2020 2020-08-29 21:58:57,557 INFO [main] com.dianwoba.common.datasource.DataSourceAspect:invoke:32 restore database connection Exception in thread "main" java.lang.RuntimeException: my test // 沒有重新拋出異常觸發(fā)重試
該參數(shù)為false時(shí)會(huì)重試3次后拋出異常,重試期間不會(huì)重新拋出異常。參數(shù)為true時(shí)則重試期間也會(huì)重新拋出異常導(dǎo)致重試失敗不再繼續(xù)重試
backoff.multiplier
注解屬性配置
@Retryable( backoff = @Backoff( delay = 1000, multiplier = 2), maxAttempts = 10)
輸出結(jié)果
## current Date:Sat Aug 29 23:06:50 CST 2020 ## current Date:Sat Aug 29 23:06:51 CST 2020 ## current Date:Sat Aug 29 23:06:53 CST 2020 ## current Date:Sat Aug 29 23:06:57 CST 2020 ## current Date:Sat Aug 29 23:07:05 CST 2020 ## current Date:Sat Aug 29 23:07:21 CST 2020 ## current Date:Sat Aug 29 23:07:51 CST 2020 ## current Date:Sat Aug 29 23:08:21 CST 2020 ## current Date:Sat Aug 29 23:08:51 CST 2020 ## current Date:Sat Aug 29 23:09:21 CST 2020 2020-08-29 23:09:21,949 INFO [main] com.dianwoba.common.datasource.DataSourceAspect:invoke:32 restore database connection Exception in thread "main" java.lang.RuntimeException: my test
乘數(shù)正確,指數(shù)型增長(zhǎng),第1次延遲1s
第2次,上次延遲1s乘以乘數(shù)2=延遲2s
第3次,上次延遲2s乘以乘數(shù)2=延遲4s
…
指數(shù)增長(zhǎng),如果沒有指定則為始終如一的固定間隔延遲類型。新版本已經(jīng)增加了各種類型單獨(dú)的屬性配置的模板構(gòu)建者:
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();
backoff.random
測(cè)試代碼
@Component
public class MyTask {
private Long lastTime = null;
@Retryable( backoff = @Backoff( delay = 1000, multiplier = 2, random = true), maxAttempts = 10)
public void doExecute(){
if (lastTime == null) {
lastTime = System.currentTimeMillis();
}
System.out.println("## actual delay:" + (System.currentTimeMillis() - lastTime) );
RuntimeException runtimeException = new RuntimeException("my test");
throw runtimeException;
}
}
輸出結(jié)果
## current Date:Sat Aug 29 22:53:10 CST 2020
## current Date:Sat Aug 29 22:53:11 CST 2020
## current Date:Sat Aug 29 22:53:14 CST 2020
## current Date:Sat Aug 29 22:53:20 CST 2020
## current Date:Sat Aug 29 22:53:29 CST 2020
## current Date:Sat Aug 29 22:53:51 CST 2020
## current Date:Sat Aug 29 22:54:41 CST 2020
## current Date:Sat Aug 29 22:55:25 CST 2020
## current Date:Sat Aug 29 22:56:11 CST 2020
## current Date:Sat Aug 29 22:57:01 CST 2020
2020-08-29 22:57:01,617 INFO [main] com.dianwoba.common.datasource.DataSourceAspect:invoke:32 restore database connection
Exception in thread "main" java.lang.RuntimeException: my test
延遲更加隨機(jī)化,由于是最大延遲為之前延遲的乘數(shù)的倍數(shù),所以看不出規(guī)律。它的使用場(chǎng)景是使延遲更加隨機(jī)化
exceptionExpression
測(cè)試代碼
@Component
public class MyTask {
private Long lastTime = null;
public boolean canRetry(RuntimeException runtimeException) {
System.out.println("canRetry:"+runtimeException.hashCode());
return true;
}
@Retryable(exceptionExpression = "#{@myTask.canRetry(#root)}", backoff = @Backoff(delay = 1000, multiplier = 2, random = true))
public void doExecute() {
if (lastTime == null) {
lastTime = System.currentTimeMillis();
}
System.out.println("## actual delay:" + (System.currentTimeMillis() - lastTime));
RuntimeException runtimeException = new RuntimeException("my test");
System.out.println("doExecute:"+runtimeException.hashCode());
throw runtimeException;
}
}
輸出結(jié)果
## actual delay:0 doExecute:626562869 2020-08-29 23:50:49,905 DEBUG [main] com.dianwoba.common.datasource.DataSourceAspect:invoke:28 public boolean com.dianwoda.billing.settle.task.MyTask.canRetry(java.lang.RuntimeException) execute with datasource is master canRetry:626562869 2020-08-29 23:50:49,906 INFO [main] com.dianwoba.common.datasource.DataSourceAspect:invoke:32 restore database connection 2020-08-29 23:50:51,335 DEBUG [main] com.dianwoba.common.datasource.DataSourceAspect:invoke:28 public boolean com.dianwoda.billing.settle.task.MyTask.canRetry(java.lang.RuntimeException) execute with datasource is master canRetry:626562869 2020-08-29 23:50:51,336 INFO [main] com.dianwoba.common.datasource.DataSourceAspect:invoke:32 restore database connection ## actual delay:1450 doExecute:90418597 2020-08-29 23:50:51,337 DEBUG [main] com.dianwoba.common.datasource.DataSourceAspect:invoke:28 public boolean com.dianwoda.billing.settle.task.MyTask.canRetry(java.lang.RuntimeException) execute with datasource is master canRetry:90418597 2020-08-29 23:50:51,338 INFO [main] com.dianwoba.common.datasource.DataSourceAspect:invoke:32 restore database connection 2020-08-29 23:50:53,620 DEBUG [main] com.dianwoba.common.datasource.DataSourceAspect:invoke:28 public boolean com.dianwoda.billing.settle.task.MyTask.canRetry(java.lang.RuntimeException) execute with datasource is master canRetry:90418597 2020-08-29 23:50:53,620 INFO [main] com.dianwoba.common.datasource.DataSourceAspect:invoke:32 restore database connection ## actual delay:3734 doExecute:307531674 2020-08-29 23:50:53,621 INFO [main] com.dianwoba.common.datasource.DataSourceAspect:invoke:32 restore database connection Exception in thread "main" java.lang.RuntimeException: my test
注意:1.2.5之后表達(dá)式的預(yù)發(fā)有所改變,詳情可以參考官方文檔:https://github.com/spring-projects/spring-retry
以上這篇spring Retryable注解實(shí)現(xiàn)重試詳解就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Mybatis-Plus-AutoGenerator 最詳細(xì)使用方法
這篇文章主要介紹了Mybatis-Plus-AutoGenerator 最詳細(xì)使用方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
Java+MySQL實(shí)現(xiàn)設(shè)計(jì)優(yōu)惠券系統(tǒng)
這篇文章主要介紹了Java+MySQL實(shí)現(xiàn)設(shè)計(jì)優(yōu)惠券系統(tǒng),文章基于Java與MySQL的相關(guān)資料展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-05-05
淺談springboot中tk.mapper代碼生成器的用法說明
這篇文章主要介紹了淺談springboot中tk.mapper代碼生成器的用法說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09
springboot整合netty框架實(shí)現(xiàn)站內(nèi)信
Netty 是一個(gè)基于NIO的客戶、服務(wù)器端編程框架,使用Netty 可以確保你快速和簡(jiǎn)單的開發(fā)出一個(gè)網(wǎng)絡(luò)應(yīng)用,這篇文章主要介紹了springboot整合netty框架的方式小結(jié),需要的朋友可以參考下2022-12-12

