Feign調(diào)用可重試的最佳方案分享
前言
在我們公司里,不同的服務(wù)之間通過Feign進(jìn)行遠(yuǎn)程調(diào)用,但是,我們?cè)趪L試使調(diào)用可重試時(shí)遇到了一個(gè)小問題,Feign框架本身可以配置的自己的重試機(jī)制,但是它是一刀切的方式,所有的調(diào)用都是同樣的機(jī)制,沒有辦法像我們希望的那樣在每個(gè)方法的基礎(chǔ)上配置。不過我在項(xiàng)目中探索除了一種新的寫法,通過spring-retry框架集合Feign去實(shí)現(xiàn)重試機(jī)制,可以為每個(gè)調(diào)用實(shí)現(xiàn)不同的重試機(jī)制,那究竟是如何做到的呢,繼續(xù)往下看呀。
自定義注解@FeignRetry
為了解決上面提到的問題,讓Feign調(diào)用的每個(gè)接口單獨(dú)配置不同的重試機(jī)制。我們使用了面向切面編程并編寫了一個(gè)自定義注解:@FeignRetry。此注釋的工作方式類似于@Retryable的包裝器,并與其共享相同的規(guī)范以避免混淆。
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface FeignRetry {
Backoff backoff() default @Backoff();
int maxAttempt() default 3;
Class<? extends Throwable>[] include() default {};
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Backoff {
long delay() default 1000L;;
long maxDelay() default 0L;
double multiplier() default 0.0D;;
}FeignRetryAspect切面處理@FeignRetry注解。
Slf4j
@Aspect
@Component
public class FeignRetryAspect {
@Around("@annotation(FeignRetry)")
public Object retry(ProceedingJoinPoint joinPoint) throws Throwable {
Method method = getCurrentMethod(joinPoint);
FeignRetry feignRetry = method.getAnnotation(FeignRetry.class);
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setBackOffPolicy(prepareBackOffPolicy(feignRetry));
retryTemplate.setRetryPolicy(prepareSimpleRetryPolicy(feignRetry));
// 重試
return retryTemplate.execute(arg0 -> {
int retryCount = arg0.getRetryCount();
log.info("Sending request method: {}, max attempt: {}, delay: {}, retryCount: {}",
method.getName(),
feignRetry.maxAttempt(),
feignRetry.backoff().delay(),
retryCount
);
return joinPoint.proceed(joinPoint.getArgs());
});
}
private BackOffPolicy prepareBackOffPolicy(FeignRetry feignRetry) {
if (feignRetry.backoff().multiplier() != 0) {
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(feignRetry.backoff().delay());
backOffPolicy.setMaxInterval(feignRetry.backoff().maxDelay());
backOffPolicy.setMultiplier(feignRetry.backoff().multiplier());
return backOffPolicy;
} else {
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(feignRetry.backoff().delay());
return fixedBackOffPolicy;
}
}
private SimpleRetryPolicy prepareSimpleRetryPolicy(FeignRetry feignRetry) {
Map<Class<? extends Throwable>, Boolean> policyMap = new HashMap<>();
policyMap.put(RetryableException.class, true); // Connection refused or time out
policyMap.put(ClientException.class, true); // Load balance does not available (cause of RunTimeException)
if (feignRetry.include().length != 0) {
for (Class<? extends Throwable> t : feignRetry.include()) {
policyMap.put(t, true);
}
}
return new SimpleRetryPolicy(feignRetry.maxAttempt(), policyMap, true);
}
private Method getCurrentMethod(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
return signature.getMethod();
}
}捕獲FeignRetry注解的方法,將配置傳遞給Spring RetryTemplate,根據(jù)配置調(diào)用服務(wù)。
@FeignRetry 的使用
用法很簡單,只需將注釋放在我們希望重試機(jī)制處于活動(dòng)狀態(tài)的 Feign Client 方法上即可。自定義切面的用法類似于Spring自帶的@Retryable注解。
@GetMapping @FeignRetry(maxAttempt = 3, backoff = @Backoff(delay = 500L)) ResponseEntity<String> retrieve1(); @GetMapping @FeignRetry(maxAttempt = 6, backoff = @Backoff(delay = 500L, maxDelay = 20000L, multiplier = 4)) ResponseEntity<String> retrieve2();
另外還需要在應(yīng)用程序類中使用 @EnableRetry 注釋來啟動(dòng)重試,比如可以加載SpringBoot的啟動(dòng)類中。
總結(jié)
Feign重試其實(shí)是一個(gè)很常見的場景,我們本文通過了自定義了一個(gè)@FeignRetry注解來實(shí)現(xiàn)可重試的機(jī)制,針對(duì)不同的Feign接口還可以使用不同的重試策略,是不是很方便,快在你的項(xiàng)目中用起來吧。
到此這篇關(guān)于Feign調(diào)用可重試的最佳方案分享的文章就介紹到這了,更多相關(guān)Feign調(diào)用可重試內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot DBUnit 單元測試(小結(jié))
這篇文章主要介紹了SpringBoot DBUnit 單元測試(小結(jié)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-09-09
Intellij IDEA 錄制快捷鍵實(shí)現(xiàn)自動(dòng)格式化的方法
這篇文章主要介紹了Intellij IDEA 錄制快捷鍵實(shí)現(xiàn)自動(dòng)格式化的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
springboot?自定義啟動(dòng)器的實(shí)現(xiàn)
本文主要介紹了springboot?自定義啟動(dòng)器的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
springcloud本地服務(wù)不注冊(cè)到注冊(cè)中心的解決方案
這篇文章主要介紹了springcloud本地服務(wù)不注冊(cè)到注冊(cè)中心,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07
SpringCloud Gateway自動(dòng)裝配實(shí)現(xiàn)流程詳解
Spring Cloud Gateway旨在為微服務(wù)架構(gòu)提供一種簡單有效的、統(tǒng)一的 API 路由管理方式。Spring Cloud Gateway 作為 Spring Cloud 生態(tài)系中的網(wǎng)關(guān),它不僅提供統(tǒng)一的路由方式,并且基于 Filter 鏈的方式提供了網(wǎng)關(guān)基本的功能,例如:安全、監(jiān)控/埋點(diǎn)和限流等2022-10-10
Java基礎(chǔ)知識(shí)精選 你答對(duì)了幾道?
精選Java基礎(chǔ)知識(shí)講解,看看你能答對(duì)多少?2017-09-09

