SpringRetry重試框架的具體使用
spring retry主要實現瞭重試和熔斷。
不適合重試的場景:
參數校驗不合法、寫操作等(要考慮寫是否冪等)都不適合重試。
適合重試的場景:
遠程調用超時、網絡突然中斷等可以重試。
在spring retry中可以指定需要重試的異常類型,並設置每次重試的間隔以及如果重試失敗是繼續重試還是熔斷(停止重試)。
一、環境搭建
加入SpringRetry依賴,SpringRetry使用AOP實現,所以也需要加入AOP包
<!-- SpringRetry --> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency>
官方文檔
二、RetryTemplate
2.1 RetryTemplate
- RetryTemplate封裝瞭Retry基本操作
- org.springframework.retry.support.RetryTemplate
- RetryTemplate中可以指定監聽、回退策略、重試策略等
- 隻需要正常new RetryTemplate()即可使用
2.2 RetryListener
RetryListener指定瞭當執行過程中出現錯誤時的回調
org.springframework.retry.RetryListener
package org.springframework.retry; public interface RetryListener { /** * 任務開始執行時調用,隻調用一次 */ <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback); /** * 任務執行結束時(包含重試)調用,隻調用一次 */ <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable); /** * 出現錯誤時回調 */ <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable); }
配置之後在RetryTemplate中指定
2.3 回退策略
2.3.1 FixedBackOffPolicy
當出現錯誤時延遲多少時間繼續調用
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); fixedBackOffPolicy.setBackOffPeriod(1000L); retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
配置之後在RetryTemplate中指定
2.3.2 ExponentialBackOffPolicy
當出現錯誤時第一次按照指定延遲時間延遲後按照指數進行延遲
// 指數回退(秒),第一次回退1s,第二次回退2s,第三次4秒,第四次8秒 ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy(); exponentialBackOffPolicy.setInitialInterval(1000L); exponentialBackOffPolicy.setMultiplier(2); retryTemplate.setBackOffPolicy(exponentialBackOffPolicy);
配置之後在RetryTemplate中指定
2.4 重試策略
重試策略主要指定出現錯誤時重試次數
// 重試策略 SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(); retryPolicy.setMaxAttempts(5); retryTemplate.setRetryPolicy(retryPolicy);
配置之後在RetryTemplate中指定
2.5 RetryCallback
RetryCallback為retryTemplate.execute時執行的回調
public final <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback) throws E
2.6 核心使用
可以使用RetryTemplate完成簡單使用
配置retryTemplate
- 指定回退策略為ExponentialBackOffPolicy
- 指定重試策略為SimpleRetryPolicy
- 指定監聽器RetryListener
import com.codecoord.util.PrintUtil; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.retry.RetryCallback; import org.springframework.retry.RetryContext; import org.springframework.retry.RetryListener; import org.springframework.retry.backoff.ExponentialBackOffPolicy; import org.springframework.retry.policy.SimpleRetryPolicy; import org.springframework.retry.support.RetryTemplate; @Configuration public class RetryTemplateConfig { /** * 註入retryTemplate */ @Bean public RetryTemplate retryTemplate() { RetryTemplate retryTemplate = new RetryTemplate(); /// 回退固定時間(秒) /* FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); fixedBackOffPolicy.setBackOffPeriod(1000L); retryTemplate.setBackOffPolicy(fixedBackOffPolicy);*/ // 指數回退(秒),第一次回退1s,第二次回退2s ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy(); exponentialBackOffPolicy.setInitialInterval(1000L); exponentialBackOffPolicy.setMultiplier(2); retryTemplate.setBackOffPolicy(exponentialBackOffPolicy); // 重試策略 SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(); retryPolicy.setMaxAttempts(5); retryTemplate.setRetryPolicy(retryPolicy); // 設置監聽器,open和close分別在啟動和結束時執行一次 RetryListener[] listeners = { new RetryListener() { @Override public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) { PrintUtil.print("open"); return true; } @Override public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { PrintUtil.print("close"); } @Override public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { PrintUtil.print("onError"); } } }; retryTemplate.setListeners(listeners); return retryTemplate; } }
在controller中註入RetryTemplate使用,也可以是在service中
@RestController public class SpringRetryController { @Resource private RetryTemplate retryTemplate; private static int count = 0; @RequestMapping("/retry") public Object retry() { try { count = 0; retryTemplate.execute((RetryCallback<Void, RuntimeException>) context -> { // 業務代碼 // .... // 模擬拋出異常 ++count; throw new RuntimeException("拋出異常"); }); } catch (RuntimeException e) { System.out.println("Exception"); } return "retry = " + count; } }
訪問retry接口,然後觀察日志輸出
18:27:20.648 – http-nio-8888-exec-1 – open
18:27:20.649 – http-nio-8888-exec-1 – retryTemplate.execute執行
18:27:20.649 – http-nio-8888-exec-1 – onError
18:27:21.658 – http-nio-8888-exec-1 – retryTemplate.execute執行
18:27:21.658 – http-nio-8888-exec-1 – onError
18:27:23.670 – http-nio-8888-exec-1 – retryTemplate.execute執行
18:27:23.670 – http-nio-8888-exec-1 – onError
18:27:27.679 – http-nio-8888-exec-1 – retryTemplate.execute執行
18:27:27.679 – http-nio-8888-exec-1 – onError
18:27:35.681 – http-nio-8888-exec-1 – retryTemplate.execute執行
18:27:35.681 – http-nio-8888-exec-1 – onError
18:27:35.681 – http-nio-8888-exec-1 – close
三、EnableRetry
@EnableRetry開啟重試,在類上指定的時候方法將默認執行,重試三次
定義service,開啟@EnableRetry註解和指定@Retryable,重試可以參考後面一節
import org.springframework.retry.annotation.Retryable; public interface RetryService { /** * 重試方法調用 */ @Retryable void retryServiceCall(); }
import org.springframework.retry.annotation.EnableRetry; import org.springframework.stereotype.Service; @EnableRetry @Service public class RetryServiceImpl implements RetryService { @Override public void retryServiceCall() { PrintUtil.print("方法調用.."); throw new RuntimeException("手工異常"); } }
controller中註入service
@RequestMapping("/retryAnnotation") public Object retryAnnotation() { retryService.retryServiceCall(); return "retryAnnotation"; }
將會默認重試
18:46:48.721 – http-nio-8888-exec-1 – 方法調用..
18:46:49.724 – http-nio-8888-exec-1 – 方法調用..
18:46:50.730 – http-nio-8888-exec-1 – 方法調用..
java.lang.RuntimeException: 手工異常
四、Retryable
用於需要重試的方法上的註解
有以下幾個屬性
Retryable註解參數
- value:指定發生的異常進行重試
- include:和value一樣,默認空,當exclude也為空時,所有異常都重試
- exclude:指定異常不重試,默認空,當include也為空時,所有異常都重試
- maxAttemps:重試次數,默認3
- backoff:重試補償機制,默認沒有
@Backoff 註解 重試補償策略
- 不設置參數時,默認使用FixedBackOffPolicy(指定等待時間),重試等待1000ms
- 設置delay,使用FixedBackOffPolicy(指定等待設置delay和maxDealy時,重試等待在這兩個值之間均態分佈)
- 設置delay、maxDealy、multiplier,使用 ExponentialBackOffPolicy(指數級重試間隔的實現),multiplier即指定延遲倍數,比如delay=5000L,multiplier=2,則第一次重試為5秒,第二次為10秒,第三次為20秒
@Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Retryable { /** * Retry interceptor bean name to be applied for retryable method. Is mutually * exclusive with other attributes. * @return the retry interceptor bean name */ String interceptor() default ""; /** * Exception types that are retryable. Synonym for includes(). Defaults to empty (and * if excludes is also empty all exceptions are retried). * @return exception types to retry */ Class<? extends Throwable>[] value() default {}; /** * Exception types that are retryable. Defaults to empty (and if excludes is also * empty all exceptions are retried). * @return exception types to retry */ Class<? extends Throwable>[] include() default {}; /** * Exception types that are not retryable. Defaults to empty (and if includes is also * empty all exceptions are retried). * If includes is empty but excludes is not, all not excluded exceptions are retried * @return exception types not to retry */ Class<? extends Throwable>[] exclude() default {}; /** * A unique label for statistics reporting. If not provided the caller may choose to * ignore it, or provide a default. * * @return the label for the statistics */ String label() default ""; /** * Flag to say that the retry is stateful: i.e. exceptions are re-thrown, but the * retry policy is applied with the same policy to subsequent invocations with the * same arguments. If false then retryable exceptions are not re-thrown. * @return true if retry is stateful, default false */ boolean stateful() default false; /** * @return the maximum number of attempts (including the first failure), defaults to 3 */ int maxAttempts() default 3; /** * @return an expression evaluated to the maximum number of attempts (including the first failure), defaults to 3 * Overrides {@link #maxAttempts()}. * @date 1.2 */ String maxAttemptsExpression() default ""; /** * Specify the backoff properties for retrying this operation. The default is a * simple {@link Backoff} specification with no properties - see it's documentation * for defaults. * @return a backoff specification */ Backoff backoff() default @Backoff(); /** * Specify an expression to be evaluated after the {@code SimpleRetryPolicy.canRetry()} * returns true - can be used to conditionally suppress the retry. Only invoked after * an exception is thrown. The root object for the evaluation is the last {@code Throwable}. * Other beans in the context can be referenced. * For example: * <pre class=code> * {@code "message.contains('you can retry this')"}. * </pre> * and * <pre class=code> * {@code "@someBean.shouldRetry(#root)"}. * </pre> * @return the expression. * @date 1.2 */ String exceptionExpression() default ""; /** * Bean names of retry listeners to use instead of default ones defined in Spring context * @return retry listeners bean names */ String[] listeners() default {}; }
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Backoff { /** * Synonym for {@link #delay()}. * * @return the delay in milliseconds (default 1000) */ long value() default 1000; /** * A canonical backoff period. Used as an initial value in the exponential case, and * as a minimum value in the uniform case. * @return the initial or canonical backoff period in milliseconds (default 1000) */ long delay() default 0; /** * The maximimum wait (in milliseconds) between retries. If less than the * {@link #delay()} then the default of * {@value org.springframework.retry.backoff.ExponentialBackOffPolicy#DEFAULT_MAX_INTERVAL} * is applied. * * @return the maximum delay between retries (default 0 = ignored) */ long maxDelay() default 0; /** * If positive, then used as a multiplier for generating the next delay for backoff. * * @return a multiplier to use to calculate the next backoff delay (default 0 = * ignored) */ double multiplier() default 0; /** * An expression evaluating to the canonical backoff period. Used as an initial value * in the exponential case, and as a minimum value in the uniform case. Overrides * {@link #delay()}. * @return the initial or canonical backoff period in milliseconds. * @date 1.2 */ String delayExpression() default ""; /** * An expression evaluating to the maximimum wait (in milliseconds) between retries. * If less than the {@link #delay()} then the default of * {@value org.springframework.retry.backoff.ExponentialBackOffPolicy#DEFAULT_MAX_INTERVAL} * is applied. Overrides {@link #maxDelay()} * * @return the maximum delay between retries (default 0 = ignored) * @date 1.2 */ String maxDelayExpression() default ""; /** * Evaluates to a vaule used as a multiplier for generating the next delay for * backoff. Overrides {@link #multiplier()}. * * @return a multiplier expression to use to calculate the next backoff delay (default * 0 = ignored) * @date 1.2 */ String multiplierExpression() default ""; /** * In the exponential case ({@link #multiplier()} > 0) set this to true to have the * backoff delays randomized, so that the maximum delay is multiplier times the * previous delay and the distribution is uniform between the two values. * * @return the flag to signal randomization is required (default false) */ boolean random() default false; }
在需要重試的方法上配置對應的重試次數、重試異常的異常類型、設置回退延遲時間、重試策略、方法監聽名稱
@Component public class PlatformClassService { @Retryable( // 重試異常的異常類型 value = {Exception.class}, // 最大重試次數 maxAttempts = 5, // 設置回退延遲時間 backoff = @Backoff(delay = 500), // 配置回調方法名稱 listeners = "retryListener" ) public void call() { System.out.println("call..."); throw new RuntimeException("手工異常"); } }
// 初始延遲2秒,然後之後驗收1.5倍延遲重試,總重試次數4 @Retryable(value = {Exception.class}, maxAttempts = 4, backoff = @Backoff(delay = 2000L, multiplier = 1.5))
監聽方法,在配置類中進行配置
/** * 註解調用 */ @Bean public RetryListener retryListener() { return new RetryListener() { @Override public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) { System.out.println("open context = " + context + ", callback = " + callback); // 返回true繼續執行後續調用 return true; } @Override public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { System.out.println("close context = " + context + ", callback = " + callback); } @Override public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { System.out.println("onError context = " + context + ", callback = " + callback); } }; }
調用服務
@RestController public class SpringRetryController { @Resource private PlatformClassService platformClassService; @RequestMapping("/retryPlatformCall") public Object retryPlatformCall() { try { platformClassService.call(); } catch (Exception e) { return "嘗試調用失敗"; } return "retryPlatformCall"; } }
調用結果
到此這篇關於SpringRetry重試框架的具體使用的文章就介紹到這瞭,更多相關SpringRetry重試框架內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- SpringBoot整合spring-retry實現接口請求重試機制及註意事項
- spring retry方法調用失敗重試機制示例解析
- springboot整合spring-retry的實現示例
- Springboot Retry組件@Recover失效問題解決方法
- 一文詳解Python中的重試機制