Feign調(diào)用可重試的最佳方案分享
前言
在我們公司里,不同的服務(wù)之間通過(guò)Feign
進(jìn)行遠(yuǎn)程調(diào)用,但是,我們?cè)趪L試使調(diào)用可重試時(shí)遇到了一個(gè)小問(wèn)題,Feign
框架本身可以配置的自己的重試機(jī)制,但是它是一刀切的方式,所有的調(diào)用都是同樣的機(jī)制,沒(méi)有辦法像我們希望的那樣在每個(gè)方法的基礎(chǔ)上配置。不過(guò)我在項(xiàng)目中探索除了一種新的寫(xiě)法,通過(guò)spring-retry
框架集合Feign
去實(shí)現(xiàn)重試機(jī)制,可以為每個(gè)調(diào)用實(shí)現(xiàn)不同的重試機(jī)制,那究竟是如何做到的呢,繼續(xù)往下看呀。
自定義注解@FeignRetry
為了解決上面提到的問(wèn)題,讓Feign調(diào)用的每個(gè)接口單獨(dú)配置不同的重試機(jī)制。我們使用了面向切面編程并編寫(xiě)了一個(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 的使用
用法很簡(jiǎn)單,只需將注釋放在我們希望重試機(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
注釋來(lái)啟動(dòng)重試,比如可以加載SpringBoot的啟動(dòng)類中。
總結(jié)
Feign
重試其實(shí)是一個(gè)很常見(jiàn)的場(chǎng)景,我們本文通過(guò)了自定義了一個(gè)@FeignRetry
注解來(lái)實(shí)現(xiàn)可重試的機(jī)制,針對(duì)不同的Feign
接口還可以使用不同的重試策略,是不是很方便,快在你的項(xiàng)目中用起來(lái)吧。
到此這篇關(guān)于Feign調(diào)用可重試的最佳方案分享的文章就介紹到這了,更多相關(guān)Feign調(diào)用可重試內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Kotlin基礎(chǔ)教程之控制流(順序,分支,循環(huán))
這篇文章主要介紹了Kotlin基礎(chǔ)教程之控制流的相關(guān)資料,需要的朋友可以參考下2017-05-05基于Java實(shí)現(xiàn)QQ郵箱發(fā)送工具類
我們?cè)谌粘i_(kāi)發(fā)中,需要實(shí)現(xiàn)一個(gè)對(duì)郵箱的發(fā)送,今天就實(shí)現(xiàn)郵箱的發(fā)送工具類,只需要一些注冊(cè)郵箱之后的配置即可,感興趣的小伙伴可以了解下2023-12-12SpringCloud+Tornado基于jwt實(shí)現(xiàn)請(qǐng)求安全校驗(yàn)功能
這篇文章主要介紹了SpringCloud+Tornado基于jwt實(shí)現(xiàn)請(qǐng)求安全校驗(yàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12SpringBoot+Redis隊(duì)列實(shí)現(xiàn)Java版秒殺的示例代碼
本文主要介紹了SpringBoot+Redis隊(duì)列實(shí)現(xiàn)Java版秒殺的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06如何自定義Mybatis-Plus分布式ID生成器(解決ID長(zhǎng)度超過(guò)JavaScript整數(shù)安全范圍問(wèn)題)
MyBatis-Plus默認(rèn)生成的是 64bit 長(zhǎng)整型,而 JS 的 Number 類型精度最高只有 53bit,這篇文章主要介紹了如何自定義Mybatis-Plus分布式ID生成器(解決ID長(zhǎng)度超過(guò)JavaScript整數(shù)安全范圍問(wèn)題),需要的朋友可以參考下2024-08-08uploadify上傳及后臺(tái)文件合法性驗(yàn)證的代碼解析
這篇文章主要介紹了uploadify上傳及后臺(tái)文件合法性驗(yàn)證的代碼解析,整段代碼分為后臺(tái)上傳方法,文件合法性驗(yàn)證類,前端上傳js,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11Java序列化中子類、父類構(gòu)造函數(shù)問(wèn)題實(shí)例分析
這篇文章主要介紹了Java序列化中子類、父類構(gòu)造函數(shù)問(wèn)題,結(jié)合實(shí)例形式分析了java父類與子類構(gòu)造函數(shù)中序列化接口調(diào)用相關(guān)操作技巧與使用注意事項(xiàng),需要的朋友可以參考下2019-09-09輕量級(jí)聲明式的Http庫(kù)——Feign的獨(dú)立使用
這篇文章主要介紹了輕量級(jí)聲明式的Http庫(kù)——Feign的使用教程,幫助大家更好的理解和學(xué)習(xí)使用feign,感興趣的朋友可以了解下2021-04-04