Feign自定义重试策略及超时时间详解

 更新时间:2023年07月28日 11:57:18   作者:罗_宇  
这篇文章主要为大家介绍了Feign自定义重试策略及超时时间详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

背景

feign可以配置重试策略及超时时间,但是无法根据业务场景动态的设置。可能会引起接口幂等,无效重试资源耗费,大数据量耗时操作报超时异常等问题。所以需要更细粒度的重试策略及超时时间配置。

自定义重试策略

框架会使用容器中RetryerRequest.Options 类型的配置Bean构造对应的feignClient Bean, 后续使用的时候可以直接通过@Autowired 注入即可发起调用;

若要进行更加灵活的控制feign,也可以手动构造FeignClient,通过构造时设置RetryerRequest.Options 可以达到 feign class 级别控制粒度;

引入全局配置Bean

由于构造FeignClient需要依赖一些Bean,所以先构造全局配置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;
    }

}

手动构造FeignClient

根据上述的配置类,构造自定义FeignClient;

此配置需要在调用方服务中定义,直接复制该配置类,根据需要模仿customRoleClient()方法实现

// 引入全局配置
@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; 【后续扩展自定义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);
    }
    /**
     * 手动构建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

由于框架会根据全局配置构造一个FeignClientBean, 上述步骤又手动构造了一个Bean,容器中存在两个相同类型RoleClient的Bean。

使用@Autowired注入需要添加@Qualifier("customRoleClient")标识唯一Bean 。

可以使用@Resource 注解,优先根据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());
    }
}

自定义超时时间

在处理大数据量、大文件以、统计等耗时任务时需要自定义超时时间,防止出现feign调用超时异常。

feignClient粒度的自定义超时

根据上文的描述,可以自定义FeignClientBean,从而将超时时间控制在client Bean粒度。

方法粒度的自定义超时

feign方法调用逻辑

feign.SynchronousMethodHandler#invoke 方法源码

//feign方法调用实现
@Override
public Object invoke(Object[] argv) throws Throwable {
  RequestTemplate template = buildTemplateFromArgs.create(argv);
  //获取当前方法的Request.Options超时配置
  Options options = findOptions(argv);
  Retryer retryer = this.retryer.clone();
  while (true) {
    try {
      //方法调用
      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方法参数列表中找到Request.Options实例对象
  Options findOptions(Object[] argv) {
    // 如果方法没有参数,使用client配置
    if (argv == null || argv.length == 0) {
      return this.options;
    }
    //查找并使用参数列表的Request.Options,若不存在则使用client配置
    return Stream.of(argv)
        .filter(Options.class::isInstance)
        .map(Options.class::cast)
        .findFirst()
        .orElse(this.options);
  }

方法定义

基于以上的代码分析,可以在feign方法签名中参数列表增加一个Request.Options 参数,在调用的时候动态构建Request.Options对象传入;

@FeignClient(value = UserConstants.SERVER_NAME)
public interface RoleClient {
    @GetMapping(value = "/openfeign/role/checkRoleDataAuth")
    ResultBody checkRoleDataAuth(@RequestParam("roleId") String roleId, Request.Options options);
}

方法调用

//自定义超时时间,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自定义重试策略及超时时间详解的详细内容,更多关于Feign自定义重试策略超时的资料请关注脚本之家其它相关文章!

相关文章

  • MybatisPlus 主键策略的几种实现方法

    MybatisPlus 主键策略的几种实现方法

    MybatisPlus-Plus支持多种主键生成策略,可以通过@TableId注解的type属性配置,主要策略包括AUTO、INPUT、ASSING_ID、ASSING_UUID和NONE,每种策略适用于不同的场景,下面就来介绍一下
    2024-10-10
  • springboot项目中利用腾讯云上传存储文件

    springboot项目中利用腾讯云上传存储文件

    本文主要介绍了springboot项目中利用腾讯云上传存储文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-12-12
  • springBoot中的CORS跨域注解@CrossOrigin详解

    springBoot中的CORS跨域注解@CrossOrigin详解

    这篇文章主要介绍了springBoot中的CORS跨域注解@CrossOrigin详解,通常,服务于 JS 的主机(例如 example.com)与服务于数据的主机(例如 api.example.com)是不同的,在这种情况下,CORS 可以实现跨域通信,需要的朋友可以参考下
    2023-12-12
  • MybatisPlus之likeRight的用法

    MybatisPlus之likeRight的用法

    这篇文章主要介绍了MybatisPlus之likeRight的用法说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Linux环境下Java项目基础环境搭建过程

    Linux环境下Java项目基础环境搭建过程

    文章主要讲述了在Linux系统下安装和配置JDK、Tomcat、MySQL的过程,包括使用tar、rpm、yum命令进行软件安装,修改环境变量,启动服务,配置防火墙,以及初始化MySQL数据库等步骤
    2026-04-04
  • Java FileWriter输出换行操作

    Java FileWriter输出换行操作

    这篇文章主要介绍了Java FileWriter输出换行操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • IDEA JetBrains Mono字体介绍和安装教程(详解)

    IDEA JetBrains Mono字体介绍和安装教程(详解)

    这篇文章主要介绍了IDEA JetBrains Mono字体介绍和安装教程,本给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-03-03
  • Java基础知识之CharArrayWriter流的使用

    Java基础知识之CharArrayWriter流的使用

    这篇文章主要介绍了Java基础知识之CharArrayWriter流的使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Java 中的整型数据类型((byte, short, int, long )的取值范围及使用不同的整型的场景分析

    Java 中的整型数据类型((byte, short, int, long )的取值范围及使用不同的整型的场景

    Java中的整型数据类型包括byte、short、int和long,每种类型都有不同的内存占用和取值范围,选择合适的整型类型取决于具体需求,如内存敏感场景、性能考量和避免整数溢出,示例代码展示了如何声明和初始化不同整型变量,并打印它们的最大值,感兴趣的朋友一起看看吧
    2025-03-03
  • 使用Spring MVC实现双向数据绑定

    使用Spring MVC实现双向数据绑定

    Spring MVC是一个广泛用于构建Java Web应用程序的框架,它提供了众多功能,包括双向数据绑定,在这篇文章中,我们将向Java新手介绍如何使用Spring MVC实现双向数据绑定,以及为什么这个特性如此重要,需要的朋友可以参考下
    2024-01-01

最新评论