Feign自定義重試策略及超時時間詳解
背景
feign可以配置重試策略及超時時間,但是無法根據(jù)業(yè)務(wù)場景動態(tài)的設(shè)置。可能會引起接口冪等,無效重試資源耗費,大數(shù)據(jù)量耗時操作報超時異常等問題。所以需要更細(xì)粒度的重試策略及超時時間配置。
自定義重試策略
框架會使用容器中Retryer
和Request.Options
類型的配置Bean構(gòu)造對應(yīng)的feignClient Bean, 后續(xù)使用的時候可以直接通過@Autowired
注入即可發(fā)起調(diào)用;
若要進(jìn)行更加靈活的控制feign,也可以手動構(gòu)造FeignClient,通過構(gòu)造時設(shè)置Retryer
和Request.Options
可以達(dá)到 feign class 級別控制粒度;
引入全局配置Bean
由于構(gòu)造FeignClient需要依賴一些Bean,所以先構(gòu)造全局配置Bean;
@Slf4j @Configuration public class FeignAutoConfiguration { public static final int CONNECT_TIME_OUT_MILLIS = 5000; public static final int READ_TIME_OUT_MILLIS = 12000; @Autowired(required = false) private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>(); @Bean public Encoder encoder(ObjectFactory<HttpMessageConverters> messageConverters) { Encoder encoder = new SpringEncoder(messageConverters); return encoder; } @Bean public Decoder decoder(ObjectFactory<HttpMessageConverters> messageConverters) { Decoder decoder = new SpringDecoder(messageConverters); return decoder; } @Bean public Contract feignContract(@Qualifier("mvcConversionService") ConversionService feignConversionService) { return new SpringMvcContract(this.parameterProcessors, feignConversionService); } //全局超時配置 @Bean public Request.Options options() { return new Request.Options(CONNECT_TIME_OUT_MILLIS, READ_TIME_OUT_MILLIS); } //全局重試策略 @Bean public Retryer feignRetryer() { return Retryer.NEVER_RETRY; } }
手動構(gòu)造FeignClient
根據(jù)上述的配置類,構(gòu)造自定義FeignClient;
此配置需要在調(diào)用方服務(wù)中定義,直接復(fù)制該配置類,根據(jù)需要模仿customRoleClient()
方法實現(xiàn)
// 引入全局配置 @Import(value = {FeignAutoConfiguration.class}) @Configuration public class CustomFeignClientConfiguration { @Qualifier("feignClient") @Autowired private Client client; @Autowired private Encoder encoder; @Autowired private Decoder decoder; @Autowired private Contract contract; @Autowired private Request.Options options; @Autowired private Retryer retryer; /** * 自定義RoleClient; 【后續(xù)擴(kuò)展自定義Feign的模仿本方法配置即可】 * * @return */ @Bean public RoleClient customRoleClient() { //自定義超時時間,connectTimeout 5s ; readTimeout 10s; Request.Options options = new Request.Options(5, TimeUnit.SECONDS, 10, TimeUnit.SECONDS, true); //重試2次 Retryer.Default retryer = new Retryer.Default(100, SECONDS.toMillis(1), 2); return getCustomFeignClient(RoleClient.class, options, retryer); } /** * 手動構(gòu)建feignClient工具方法 * * @param clazz * @param options * @param retryer * @param <T> * @return */ private <T> T getCustomFeignClient(Class<T> clazz, Request.Options options, Retryer retryer) { //只需要對其中的超時和重試配置自定義,其他的還需要使用全局配置 //通過反射獲取@FeignClient注解 FeignClient annotation = clazz.getAnnotation(FeignClient.class); return Feign.builder() .client(client) .options(options == null ? this.options : options) .retryer(retryer == null ? this.retryer : retryer) .contract(contract) .encoder(encoder) .decoder(decoder) .target(clazz, "http://" + annotation.value()); } }
使用自定義FeignClient
由于框架會根據(jù)全局配置構(gòu)造一個FeignClientBean, 上述步驟又手動構(gòu)造了一個Bean,容器中存在兩個相同類型RoleClient
的Bean。
使用@Autowired
注入需要添加@Qualifier("customRoleClient")
標(biāo)識唯一Bean 。
可以使用@Resource
注解,優(yōu)先根據(jù)beanName注入。
// 注入 @Resource private RoleClient roleClient; @Resource private RoleClient customRoleClient; public void checkRoleDataAuth(String roleId){ // 使用時直接替換feignClient即可 // ResultBody resultBody = roleClient.checkRoleDataAuth(roleId); ResultBody resultBody = customRoleClient.checkRoleDataAuth(roleId); if (!resultBody.isSuccess()){ throw new BaseException(resultBody.getCode(),resultBody.getMessage()); } }
自定義超時時間
在處理大數(shù)據(jù)量、大文件以、統(tǒng)計等耗時任務(wù)時需要自定義超時時間,防止出現(xiàn)feign調(diào)用超時異常。
feignClient粒度的自定義超時
根據(jù)上文的描述,可以自定義FeignClientBean,從而將超時時間控制在client Bean粒度。
方法粒度的自定義超時
feign方法調(diào)用邏輯
feign.SynchronousMethodHandler#invoke
方法源碼
//feign方法調(diào)用實現(xiàn) @Override public Object invoke(Object[] argv) throws Throwable { RequestTemplate template = buildTemplateFromArgs.create(argv); //獲取當(dāng)前方法的Request.Options超時配置 Options options = findOptions(argv); Retryer retryer = this.retryer.clone(); while (true) { try { //方法調(diào)用 return executeAndDecode(template, options); } catch (RetryableException e) { //重試 try { retryer.continueOrPropagate(e); } catch (RetryableException th) { Throwable cause = th.getCause(); if (propagationPolicy == UNWRAP && cause != null) { throw cause; } else { throw th; } } if (logLevel != Logger.Level.NONE) { logger.logRetry(metadata.configKey(), logLevel); } continue; } } } //從feignClient方法參數(shù)列表中找到Request.Options實例對象 Options findOptions(Object[] argv) { // 如果方法沒有參數(shù),使用client配置 if (argv == null || argv.length == 0) { return this.options; } //查找并使用參數(shù)列表的Request.Options,若不存在則使用client配置 return Stream.of(argv) .filter(Options.class::isInstance) .map(Options.class::cast) .findFirst() .orElse(this.options); }
方法定義
基于以上的代碼分析,可以在feign方法簽名中參數(shù)列表增加一個Request.Options
參數(shù),在調(diào)用的時候動態(tài)構(gòu)建Request.Options
對象傳入;
@FeignClient(value = UserConstants.SERVER_NAME) public interface RoleClient { @GetMapping(value = "/openfeign/role/checkRoleDataAuth") ResultBody checkRoleDataAuth(@RequestParam("roleId") String roleId, Request.Options options); }
方法調(diào)用
//自定義超時時間,connectTimeout 5s ; readTimeout 60s; Request.Options options = new Request.Options(5, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true); ResultBody resultBody = roleClient.checkRoleDataAuth(roleId, options); //傳入null,使用client中的超時配置 ResultBody resultBody = roleClient.checkRoleDataAuth(roleId, null);
以上就是Feign自定義重試策略及超時時間詳解的詳細(xì)內(nèi)容,更多關(guān)于Feign自定義重試策略超時的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
如何解決java.util.concurrent.CancellationException問題
這篇文章主要介紹了如何解決java.util.concurrent.CancellationException問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05SpringBoot整合Netty+Websocket實現(xiàn)消息推送的示例代碼
WebSocket使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡單,允許服務(wù)端主動向客戶端推送數(shù)據(jù),本文主要介紹了SpringBoot整合Netty+Websocket實現(xiàn)消息推送的示例代碼,具有一定的參考價值,感興趣的可以了解一下2024-01-01解決IDEA中下載free maven plugin插件無效的問題
這篇文章主要介紹了解決IDEA中下載free maven plugin插件無效的問題,本文通過圖文并茂的形式給大家分享解決方案,供大家參考,需要的朋友可以參考下2020-11-11Java中生成隨機(jī)數(shù)的實現(xiàn)方法總結(jié)
這篇文章主要介紹了Java中生成隨機(jī)數(shù)的實現(xiàn)方法總結(jié),其中多線程并發(fā)的實現(xiàn)方式尤為exciting,需要的朋友可以參考下2015-11-11IDEA中如何查找jar包之間的依賴關(guān)系并忽略依賴的某個包
這篇文章主要介紹了IDEA中如何查找jar包之間的依賴關(guān)系并忽略依賴的某個包?本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08