1.基本概念
1.1應(yīng)用場景
1.1.1 數(shù)據(jù)同步
有時候項目需要進(jìn)行同步數(shù)據(jù)(定時任務(wù)),一定要同步成功,不然對于業(yè)務(wù)會有影響锦积,偶發(fā)性的會出現(xiàn)調(diào)用接口失敗,失敗并不是特別多东羹,一般的流程如下:
(1)循環(huán)的進(jìn)行遠(yuǎn)程調(diào)用番电,同步數(shù)據(jù)容达,記錄一下調(diào)用失敗的記錄
(2)休眠一段時間构舟,繼續(xù)循環(huán)調(diào)用失敗的記錄
(3)如果再調(diào)用失敗洪碳、通過人工二次調(diào)用進(jìn)行修復(fù)
1.1.2 拋出xxx異诚廖眨或者返回結(jié)果為x 需要重試
比如:遠(yuǎn)程調(diào)用超時闪金、網(wǎng)絡(luò)突然中斷等可以重試
1.2 重試框架需要解決的問題
1.2.1 重試的策略(RetryPolicy)
無限重試?最多重試幾次论颅、指定的時間范圍內(nèi)可以重試哎垦、或者多種重試策略組合。
1.2.2 重試的要休眠多久(BackOffPolicy)
重試時間間隔恃疯,每次都休眠固定的時間撼泛、第一次1s 第二次2s 第三次4s 、隨機(jī)的休眠時間
1.2.3兜底方案(Recover)
如果所有的重試都失敗了澡谭、兜底方案是什么愿题?有點類似限流,最差返回你系統(tǒng)繁忙的界面。
2.spring retry
Spring Retry 是從 Spring batch 中獨立出來的一個功能蛙奖,主要實現(xiàn)了重試和熔斷潘酗,對于那些重試后不會改變結(jié)果,毫無意義的操作雁仲,不建議使用重試仔夺。spring retry提供了注解和編程 兩種支持,提供了 RetryTemplate 支持攒砖,類似RestTemplate缸兔。整個流程如下:
具體使用過程中涉及的核心對象有:
RetryTemplate: 封裝了Retry基本操作日裙,是進(jìn)入spring-retry框架的整體流程入口,通過RetryTemplate可以指定監(jiān)聽、回退策略惰蜜、重試策略等昂拂。
RetryCallback:該接口封裝了業(yè)務(wù)代碼,且failback后抛猖,會再次調(diào)用RetryCallback接口
RetryPolicy:重試策略格侯,描述將以什么樣的方式調(diào)用RetryCallback接口
BackOffPolicy :回退策略,當(dāng)出現(xiàn)錯誤時延遲多少時間繼續(xù)調(diào)用
2.1 添加依賴
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.1.5.RELEASE</version>
</dependency>
2.2 使用步驟
(1)定義重試策略RetryPolicy
實際過程如果不定義财著,則默認(rèn)SimpleRetryPolicy策略(重試3次)联四。重試策略有以下種:
NeverRetryPolicy:只允許調(diào)用RetryCallback一次,不允許重試撑教;
AlwaysRetryPolicy:允許無限重試朝墩,直到成功,此方式邏輯不當(dāng)會導(dǎo)致死循環(huán)伟姐;
SimpleRetryPolicy:固定次數(shù)重試策略鱼辙,默認(rèn)重試最大次數(shù)為3次,RetryTemplate默認(rèn)使用的策略玫镐;
TimeoutRetryPolicy:超時時間重試策略,默認(rèn)超時時間為1秒怠噪,在指定的超時時間內(nèi)允許重試恐似;
CircuitBreakerRetryPolicy:有熔斷功能的重試策略,需設(shè)置3個參數(shù)openTimeout傍念、resetTimeout和delegate矫夷;
CompositeRetryPolicy:組合重試策略,有兩種組合方式憋槐,樂觀組合重試策略是指只要有一個策略允許重試即
// 重試策略,指定重試5次
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(5);
retryTemplate.setRetryPolicy(retryPolicy);
配置之后在RetryTemplate中指定
(2)定義退避策略(BackOffPolicy )
策略主要有以下幾種:
FixedBackOffPolicy 固定時間
ExponentialBackOffPolicy 指數(shù)退避策略
ExponentialRandomBackOffPolicy 指數(shù)隨機(jī)退避策略
RetryTemplate retryTemplate = new RetryTemplate();
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(3000);
backOffPolicy.setMultiplier(2);
backOffPolicy.setMaxInterval(15000);
retryTemplate.setBackOffPolicy(backOffPolicy);
配置之后在RetryTemplate中指定
(3)RetryTemplate執(zhí)行整體流程
RetryTemplate中指定回退策略為ExponentialBackOffPolicy双藕,指定重試策略為SimpleRetryPolicy,執(zhí)行操作使用(RetryCallback 執(zhí)行業(yè)務(wù)邏輯 ,RecoveryCallback 兜底)阳仔。這里面需要用到以下核心對象
RetryCallback :業(yè)務(wù)回調(diào)入口忧陪,為retryTemplate.execute時執(zhí)行的回調(diào)
RecoveryCallback :兜底回調(diào)入口
RetryContext 重試上下文
//execute接受兩個參數(shù)(回調(diào)函數(shù))業(yè)務(wù)回調(diào)和兜底回調(diào)
RefundApplicationFormrefundApplicationForm = retryTemplate.execute(
//RetryCallback
(RetryCallback<RefundApplicationForm, Throwable>) retryContext -> bankAutoRepay(mRefundApplicationForm,
supplierBankId),
//RecoveryCallback兜底
retryContext -> {
// 銀行多次重試后異常
mRefundApplicationForm.setRepayStatCd("100");
logger.error("銀行多次重試后異常、銀行自動退款異常");
return mRefundApplicationForm;
}
);
3.spring retry 注解方式
3.1 啟用Spring Retry支持
為了啟用Spring Retry的功能近范,需要向配置類添加@EnableRetry注釋嘶摊。
@SpringBootApplication
@EnableRetry
public class Launcher {
public static void main(String[] args) {
SpringApplication.run(Launcher.class, args);
}
3.2 啟用重試特性的方法上使用@Retryable注釋
通過此注解設(shè)置重試策略和回退策略。Retryable注解參數(shù):
(1)value:指定發(fā)生的異常進(jìn)行重試
(2)include:和value一樣评矩,默認(rèn)空叶堆,當(dāng)exclude也為空時,所有異常都重試
(3)exclude:指定異常不重試斥杜,默認(rèn)空虱颗,當(dāng)include也為空時沥匈,所有異常都重試
(4)maxAttemps:重試次數(shù),默認(rèn)3
(5)backoff:重試補償機(jī)制忘渔,默認(rèn)沒有
/**
* 指定異常CustomRetryException重試高帖,重試最大次數(shù)為4(默認(rèn)是3),重試補償機(jī)制間隔200毫秒
* 還可以配置exclude,指定異常不充實,默認(rèn)為空
* @return result
* @throws CustomRetryException 指定異常
*/
@Retryable(value = {CustomRetryException.class},maxAttempts = 4,backoff = @Backoff(200))
String retry() throws CustomRetryException;
@Backoff 注解 重試補償策略:
(1)不設(shè)置參數(shù)時辨萍,默認(rèn)使用FixedBackOffPolicy(指定等待時間)棋恼,重試等待1000ms
(2)設(shè)置delay,使用FixedBackOffPolicy(指定等待設(shè)置delay和maxDealy時锈玉,重試等待在這兩個值之間均態(tài)分布)
(3)設(shè)置delay爪飘、maxDealy、multiplier拉背,使用 ExponentialBackOffPolicy(指數(shù)級重試間隔的實現(xiàn))师崎,multiplier即指定延遲倍數(shù),比如delay=5000L椅棺,multiplier=2,則第一次重試為5秒犁罩,第二次為10秒,第三次為20秒
3.3 @Recover
重試多次失敗后两疚,執(zhí)行兜底方案
@Service
@Slf4j
public class RetryServiceImpl implements RetryService {
private static int count = 1;
@Override
public String retry() throws CustomRetryException {
log.info("retry{},throw CustomRetryException in method retry",count);
count ++;
throw new CustomRetryException("throw custom exception");
}
@Recover
public String recover(Throwable throwable) {
log.info("Default Retry service test");
return "Error Class :: " + throwable.getClass().getName();
}
}
通過Junit進(jìn)行單元測試床估。
@Test
void retry() {
try {
final String message = retryService.retry();
log.info("message = "+message);
} catch (CustomRetryException e) {
log.error("Error while executing test {}",e.getMessage());
}