SpringRetry重試框架的具體使用
spring retry主要實(shí)現(xiàn)了重試和熔斷。
不適合重試的場(chǎng)景:
參數(shù)校驗(yàn)不合法、寫操作等(要考慮寫是否冪等)都不適合重試。
適合重試的場(chǎng)景:
遠(yuǎn)程調(diào)用超時(shí)、網(wǎng)絡(luò)突然中斷等可以重試。
在spring retry中可以指定需要重試的異常類型,并設(shè)置每次重試的間隔以及如果重試失敗是繼續(xù)重試還是熔斷(停止重試)。
一、環(huán)境搭建
加入SpringRetry依賴,SpringRetry使用AOP實(shí)現(xiàn),所以也需要加入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中可以指定監(jiān)聽(tīng)、回退策略、重試策略等
- 只需要正常new RetryTemplate()即可使用
2.2 RetryListener
RetryListener指定了當(dāng)執(zhí)行過(guò)程中出現(xiàn)錯(cuò)誤時(shí)的回調(diào)
org.springframework.retry.RetryListener
package org.springframework.retry;
public interface RetryListener {
/**
* 任務(wù)開(kāi)始執(zhí)行時(shí)調(diào)用,只調(diào)用一次
*/
<T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback);
/**
* 任務(wù)執(zhí)行結(jié)束時(shí)(包含重試)調(diào)用,只調(diào)用一次
*/
<T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);
/**
* 出現(xiàn)錯(cuò)誤時(shí)回調(diào)
*/
<T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);
}
配置之后在RetryTemplate中指定
2.3 回退策略
2.3.1 FixedBackOffPolicy
當(dāng)出現(xiàn)錯(cuò)誤時(shí)延遲多少時(shí)間繼續(xù)調(diào)用
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); fixedBackOffPolicy.setBackOffPeriod(1000L); retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
配置之后在RetryTemplate中指定
2.3.2 ExponentialBackOffPolicy
當(dāng)出現(xiàn)錯(cuò)誤時(shí)第一次按照指定延遲時(shí)間延遲后按照指數(shù)進(jìn)行延遲
// 指數(shù)回退(秒),第一次回退1s,第二次回退2s,第三次4秒,第四次8秒 ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy(); exponentialBackOffPolicy.setInitialInterval(1000L); exponentialBackOffPolicy.setMultiplier(2); retryTemplate.setBackOffPolicy(exponentialBackOffPolicy);
配置之后在RetryTemplate中指定
2.4 重試策略
重試策略主要指定出現(xiàn)錯(cuò)誤時(shí)重試次數(shù)
// 重試策略 SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(); retryPolicy.setMaxAttempts(5); retryTemplate.setRetryPolicy(retryPolicy);
配置之后在RetryTemplate中指定
2.5 RetryCallback
RetryCallback為retryTemplate.execute時(shí)執(zhí)行的回調(diào)
public final <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback) throws E

2.6 核心使用
可以使用RetryTemplate完成簡(jiǎn)單使用
配置retryTemplate
- 指定回退策略為ExponentialBackOffPolicy
- 指定重試策略為SimpleRetryPolicy
- 指定監(jiān)聽(tīng)器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();
/// 回退固定時(shí)間(秒)
/* FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(1000L);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);*/
// 指數(shù)回退(秒),第一次回退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);
// 設(shè)置監(jiān)聽(tīng)器,open和close分別在啟動(dòng)和結(jié)束時(shí)執(zhí)行一次
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 -> {
// 業(yè)務(wù)代碼
// ....
// 模擬拋出異常
++count;
throw new RuntimeException("拋出異常");
});
} catch (RuntimeException e) {
System.out.println("Exception");
}
return "retry = " + count;
}
}
訪問(wèn)retry接口,然后觀察日志輸出
18:27:20.648 - http-nio-8888-exec-1 - open
18:27:20.649 - http-nio-8888-exec-1 - retryTemplate.execute執(zhí)行
18:27:20.649 - http-nio-8888-exec-1 - onError
18:27:21.658 - http-nio-8888-exec-1 - retryTemplate.execute執(zhí)行
18:27:21.658 - http-nio-8888-exec-1 - onError
18:27:23.670 - http-nio-8888-exec-1 - retryTemplate.execute執(zhí)行
18:27:23.670 - http-nio-8888-exec-1 - onError
18:27:27.679 - http-nio-8888-exec-1 - retryTemplate.execute執(zhí)行
18:27:27.679 - http-nio-8888-exec-1 - onError
18:27:35.681 - http-nio-8888-exec-1 - retryTemplate.execute執(zhí)行
18:27:35.681 - http-nio-8888-exec-1 - onError
18:27:35.681 - http-nio-8888-exec-1 - close
三、EnableRetry
@EnableRetry開(kāi)啟重試,在類上指定的時(shí)候方法將默認(rèn)執(zhí)行,重試三次
定義service,開(kāi)啟@EnableRetry注解和指定@Retryable,重試可以參考后面一節(jié)
import org.springframework.retry.annotation.Retryable;
public interface RetryService {
/**
* 重試方法調(diào)用
*/
@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("方法調(diào)用..");
throw new RuntimeException("手工異常");
}
}
controller中注入service
@RequestMapping("/retryAnnotation")
public Object retryAnnotation() {
retryService.retryServiceCall();
return "retryAnnotation";
}
將會(huì)默認(rèn)重試
18:46:48.721 - http-nio-8888-exec-1 - 方法調(diào)用..
18:46:49.724 - http-nio-8888-exec-1 - 方法調(diào)用..
18:46:50.730 - http-nio-8888-exec-1 - 方法調(diào)用..
java.lang.RuntimeException: 手工異常
四、Retryable
用于需要重試的方法上的注解
有以下幾個(gè)屬性
Retryable注解參數(shù)
- value:指定發(fā)生的異常進(jìn)行重試
- include:和value一樣,默認(rèn)空,當(dāng)exclude也為空時(shí),所有異常都重試
- exclude:指定異常不重試,默認(rèn)空,當(dāng)include也為空時(shí),所有異常都重試
- maxAttemps:重試次數(shù),默認(rèn)3
- backoff:重試補(bǔ)償機(jī)制,默認(rèn)沒(méi)有
@Backoff 注解 重試補(bǔ)償策略
- 不設(shè)置參數(shù)時(shí),默認(rèn)使用FixedBackOffPolicy(指定等待時(shí)間),重試等待1000ms
- 設(shè)置delay,使用FixedBackOffPolicy(指定等待設(shè)置delay和maxDealy時(shí),重試等待在這兩個(gè)值之間均態(tài)分布)
- 設(shè)置delay、maxDealy、multiplier,使用 ExponentialBackOffPolicy(指數(shù)級(jí)重試間隔的實(shí)現(xiàn)),multiplier即指定延遲倍數(shù),比如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;
}
在需要重試的方法上配置對(duì)應(yīng)的重試次數(shù)、重試異常的異常類型、設(shè)置回退延遲時(shí)間、重試策略、方法監(jiān)聽(tīng)名稱
@Component
public class PlatformClassService {
@Retryable(
// 重試異常的異常類型
value = {Exception.class},
// 最大重試次數(shù)
maxAttempts = 5,
// 設(shè)置回退延遲時(shí)間
backoff = @Backoff(delay = 500),
// 配置回調(diào)方法名稱
listeners = "retryListener"
)
public void call() {
System.out.println("call...");
throw new RuntimeException("手工異常");
}
}
// 初始延遲2秒,然后之后驗(yàn)收1.5倍延遲重試,總重試次數(shù)4
@Retryable(value = {Exception.class}, maxAttempts = 4, backoff = @Backoff(delay = 2000L, multiplier = 1.5))
監(jiān)聽(tīng)方法,在配置類中進(jìn)行配置
/**
* 注解調(diào)用
*/
@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繼續(xù)執(zhí)行后續(xù)調(diào)用
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);
}
};
}
調(diào)用服務(wù)
@RestController
public class SpringRetryController {
@Resource
private PlatformClassService platformClassService;
@RequestMapping("/retryPlatformCall")
public Object retryPlatformCall() {
try {
platformClassService.call();
} catch (Exception e) {
return "嘗試調(diào)用失敗";
}
return "retryPlatformCall";
}
}
調(diào)用結(jié)果

到此這篇關(guān)于SpringRetry重試框架的具體使用的文章就介紹到這了,更多相關(guān)SpringRetry重試框架內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot配置全局跨域未生效,訪問(wèn)接口報(bào)錯(cuò)問(wèn)題及解決
這篇文章主要介紹了Springboot配置全局跨域未生效,訪問(wèn)接口報(bào)錯(cuò)問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
RabbitMQ 的消息持久化與 Spring AMQP 的實(shí)現(xiàn)詳解
這篇文章主要介紹了RabbitMQ 的消息持久化與 Spring AMQP 的實(shí)現(xiàn)剖析詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08
Java Hutool 包工具類推薦 ExcelUtil詳解
這篇文章主要介紹了Java Hutool 包工具類推薦 ExcelUtil詳解,需要引入hutool包,版本號(hào)可根據(jù)實(shí)際情況更換,除hutool包之外,還需要引入操作Excel必要包,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09
java進(jìn)階解析Springboot上傳excel存入數(shù)據(jù)庫(kù)步驟
項(xiàng)目需要,寫了一個(gè),批量導(dǎo)入的接口。因?yàn)樾枰褂胑xcel去批量導(dǎo)入數(shù)據(jù),所以寫了一個(gè)例子,經(jīng)過(guò)測(cè)試已經(jīng)可以用于實(shí)際開(kāi)發(fā),這里記錄一下2021-09-09
通過(guò)代理類實(shí)現(xiàn)java連接數(shù)據(jù)庫(kù)(使用dao層操作數(shù)據(jù))實(shí)例分享
java通過(guò)代理類實(shí)現(xiàn)數(shù)據(jù)庫(kù)DAO操作代碼分享,大家參考使用吧2013-12-12
Eclipse手動(dòng)導(dǎo)入DTD文件實(shí)現(xiàn)方法解析
這篇文章主要介紹了Eclipse手動(dòng)導(dǎo)入DTD文件實(shí)現(xiàn)方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10
如何使用兩個(gè)棧實(shí)現(xiàn)隊(duì)列Java
這篇文章主要介紹了如何使用兩個(gè)棧實(shí)現(xiàn)隊(duì)列Java,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
基于java socket實(shí)現(xiàn) 聊天小程序
這篇文章主要介紹了基于java socket實(shí)現(xiàn) 聊天小程序,代碼分為服務(wù)器和客戶端,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12

