spring Retryable註解實現重試詳解
spring-boot:1.5.3.RELEASE,spring-retry-1.2.0.RELEASE
使用方法
引入pom
// 版本號繼承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 { /** * 為重試方法應用重試攔截器的bean名稱。與其他屬性互斥 */ String interceptor() default ""; /** * 可以重試的異常型別。與includes屬性同義。預設值為空(並且如果exclude也是空的話, * 所有的異常都會重試) */ Class<? extends Throwable>[] value() default {}; /** * 同上 */ Class<? extends Throwable>[] include() default {}; /** * 與include含義相反 */ Class<? extends Throwable>[] exclude() default {}; /** * 統計報告的唯一標籤。如果沒有提供,呼叫者可以選擇忽略它,或者提供一個預設值。 * * @return the label for the statistics */ String label() default ""; /** * 標識重試有狀態的:即異常重新丟擲,但是重試策略使用相同的策略應用於後續的具有相同引數的 * 呼叫。如果為false那麼可重試的異常不會重新丟擲。 */ boolean stateful() default false; /** * 嘗試的最大次數(包含第一次失敗),預設為3 */ int maxAttempts() default 3; /** * 返回一個求嘗試最大次數值的表示式(包含第一次失敗),預設為3 * 重寫 {@link #maxAttempts()}。 * @since 1.2 */ String maxAttemptsExpression() default ""; /** * 為正重試的動作指定backoff屬性。預設沒有backoff,但是在兩次嘗試之間暫定一下是一個很好的想法 * (即使代價是阻塞執行緒) */ Backoff backoff() default @Backoff(); /** * 在{@code SimpleRetryPolicy.canRetry()}返回true之後指定一個計算表示式 - 可用來有條件的取消重試。 * 僅在呼叫丟擲一個異常後。求值的root物件為上一次的異常 {@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()} 屬性同義 * * 返回延遲多少毫秒後重試(預設為1000毫秒) */ long value() default 1000; /** * 一個標準的再重試周期。在指數函式情況下用作初始值,在始終如一的情況下(固定週期值情況) * 用作最小值。 * @return the initial or canonical backoff period in milliseconds (default 1000)??? */ long delay() default 0; /** * 重試之間最大等待(毫秒)時間。如果小於 {@link #delay()} 則忽略。 * @return the maximum delay between retries (default 0 = ignored) */ long maxDelay() default 0; /** * 如果是正數,則用於生成下次再重試等待時間的乘數。 * 返回一個乘數用於計算下次再重試延遲(預設為0忽略) */ double multiplier() default 0; /** * 標準再重試周期求值表示式。在指數情況下用作初始值,始終如一的情況下用作最小值。 * 重寫 {@link #delay()}. * @since 1.2 */ String delayExpression() default ""; /** * 在重試之間最大等待(毫秒)數的求值表示式。 * 如果小於 {@link #delay()} 則忽略。 * 重寫 {@link #maxDelay()} * 預設為0,忽略 * @since 1.2 */ String maxDelayExpression() default ""; /** * 表示式求值作為生成下次再重試延遲的乘數 * 重寫 {@link #multiplier()}。 * @since 1.2 */ String multiplierExpression() default ""; /** * 在指數情況下 ({@link #multiplier()} > 0) 設定該值為true將使再重試延遲隨機化, * 使最大延遲為先前延遲的乘數倍數,並使這兩個延遲值之間分佈均勻。 * 預設為false */ boolean random() default false; }
案例
預設retry
@Component public class MyTask { @Retryable public void doExecute(){ System.out.println("## current Date:" + new Date()); throw new RuntimeException("my test"); } }
輸出結果
## 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(){
輸出結果
## 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 // 沒有重新丟擲異常觸發重試
該引數為false時會重試3次後丟擲異常,重試期間不會重新丟擲異常。引數為true時則重試期間也會重新丟擲異常導致重試失敗不再繼續重試
backoff.multiplier
註解屬性配置
@Retryable( backoff = @Backoff( delay = 1000,multiplier = 2),maxAttempts = 10)
輸出結果
## 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
乘數正確,指數型增長,第1次延遲1s
第2次,上次延遲1s乘以乘數2=延遲2s
第3次,上次延遲2s乘以乘數2=延遲4s
…
指數增長,如果沒有指定則為始終如一的固定間隔延遲型別。新版本已經增加了各種型別單獨的屬性配置的模板構建者:
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
測試程式碼
@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; } }
輸出結果
## 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
延遲更加隨機化,由於是最大延遲為之前延遲的乘數的倍數,所以看不出規律。它的使用場景是使延遲更加隨機化
exceptionExpression
測試程式碼
@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,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; } }
輸出結果
## 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之後表示式的預發有所改變,詳情可以參考官方文件:https://github.com/spring-projects/spring-retry
以上這篇spring Retryable註解實現重試詳解就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。