Feign自定義重試策略及超時時間詳解
背景
feign可以配置重試策略及超時時間,但是無法根據(jù)業(yè)務(wù)場景動態(tài)的設(shè)置??赡軙鸾涌趦绲?,無效重試資源耗費(fèi),大數(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-05
SpringBoot整合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-11
Java中生成隨機(jī)數(shù)的實現(xiàn)方法總結(jié)
這篇文章主要介紹了Java中生成隨機(jī)數(shù)的實現(xiàn)方法總結(jié),其中多線程并發(fā)的實現(xiàn)方式尤為exciting,需要的朋友可以參考下2015-11-11
IDEA中如何查找jar包之間的依賴關(guān)系并忽略依賴的某個包
這篇文章主要介紹了IDEA中如何查找jar包之間的依賴關(guān)系并忽略依賴的某個包?本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08

