• 不过,默认情况下Feign还是需要结合Ribbon来使用的

    如果你只想单独使用Feign,那么就设置一下@FeignClient注解的url属性,指定请求的地址和端口就可以了

    所以,既然只是包装,前面提到的两种方式设置超时时间当然可以继续使用:

    方法参数设置形式跟前面提到的一模一样,但是通过Feign.Builder来设置却不太一样

    由于SpringCloud会自己创建Feign.Builder,不需要我们创建,所以在设置Options时,Spring提供了两种快捷方式来设置

    不过最终还是设置到Feign.Builder中

    1、声明一个Options Bean

    Spring在构建Feign.Builder的时,会从容器中查找Options这个Bean,然后设置到Feign.Builder

    @Configuration
    public class FeignConfiguration {

        @Bean
        public Request.Options options() {
            return new Request.Options(8, TimeUnit.SECONDS, 8, TimeUnit.SECONDS, true);
        }

    }

    此时debug就可以看到设置到Feign.Builder的代码

    2、配置文件中设置

    除了声明Bean之外,Spring还提供了通过配置文件的方式配置,如下:

    feign:
      client:
        config:
          default:
            connectTimeout: 10000
            readTimeout: 10000

    同样地,debug就可以看见

    声明Bean和配置文件都可以设置,那么同时设置哪种优先级高呢?

    无特殊配置,遵守SpringBoot本身的配置规定

    所以基于这个规定,配置文件的配置优先级大于手动声明Bean的优先级。

    到这,我们又学到了两种Spring为了方便我们设置Feign.Builder提供的配置方式:

    把他们俩加到前面画的图中

    所以,如果你使用了SpringCloud提供的方式来使用Feign,那么就可以通过声明OptionsBean和配置文件的方式更加方便地来设置超时时间

    最终其实还是通过Feign.Builder来设置的

    SpringCloud下通过Ribbon来设置

    当Feign配合Ribbon使用时,除了上面两种方式之外,还可以通过Ribbon来设置超时时间。

    但是这里我不知道你会不会好奇

    其实这跟Ribbon的定位有关,除了负载均衡组件之外,Ribbon也干发送Http请求的事,也就是不配合Feign,他照样可以发送http请求。

    来个简单demo

    解释一下上面的代码意思

    这样,此时就会从两个服务实例中根据负载均衡选取一个服务地址发送http请求,

    Ribbon既然可以发送Http请求,那么自然而然就可以设置超时时间

    Feign在整合Ribbon的时候,为了统一配置,就默认将自己的超时时间交由Ribbon管理

    所以,在默认情况下,Feign的超时时间可以由Ribbon配置

    而Ribbon默认连接和读超时时间只有1s,所以在默认情况下,Feign的超时时间只有1s。

    所以,在默认情况下,很容易就发生超时,不过我们可以通过配置文件修改即可

    ribbon:
      ConnectTimeout: 5000
      ReadTimeout: 5000

    你知道你发现没,上面说通过Ribbon设置Feign的超时时间,一直提到前面一直提到这个词

    什么情况下叫默认呢?

    所谓的默认,就是当你不主动设置Feign的超时时间的时候,就是默认。

    换句话说,一旦你通过上面说的那些配置方式设置Feign的超时时间,就不是默认了

    此时通过Ribbon设置的超时时间就不会生效了

    Feign是如何在默认情况下将超时时间交给Ribbon管理的?

    要想回答这个问题,就得先搬出前面反复提到的Client接口了。

    在SpringCloud的环境下,有一个Client的实现,叫LoadBalancerFeignClient

    通过名字就可以看出,带有负载均衡的Client实现,负载均衡的实现肯定是交给Ribbon来实现的

    所以当Feign配合Ribbon时用的就是这个Client实现

    既然实现了Client接口,那就看看execute方法的实现逻辑

    图中getClientConfig方法就是判断使用Feign或者Ribbon配置的核心逻辑

    核心的判断逻辑就是这一行

    DEFAULT_OPTIONS就是一个超时时间的常量

    当上述判断条件成立时,就会通过this.clientFactory.getClientConfig(clientName)获取到Ribbon配置

    由于这是Ribbon的逻辑,这里就不深扒了,知道是这个意思就行

    当条件不成立时,用Options构建一个FeignOptionsClientConfig

    FeignOptionsClientConfig就是简单地将Options配置读出来,设置到父类DefaultClientConfigImpl超时时间配置上

    DefaultClientConfigImpl就算你不知道是什么也无所谓,你能看出的一件事就是,超时时间用的是传递给ClientOptions参数

    所以,综上,我们的问题就变得非常easy了,那就是什么时候

    只有当这个条件成立时,才使用Ribbon的配置。

    这里我们先来捋一捋前面提到的东西

    前面我们反复提到,ClientOptions最终只来自于两种配置

    所以DEFAULT_OPTIONS这个Options一定是通过上面两种方法中的其中一种设置的

    而方法参数是不可能设置的成DEFAULT_OPTIONS

    因为这是我们控制的,只要我们参数不传DEFAULT_OPTIONS,那么永远都不可能是DEFAULT_OPTIONS

    此时只剩下一种情况,那就是Spring在构建在Feign.Builder的时候,设置成DEFAULT_OPTIONS

    通过查找DEFAULT_OPTIONS的使用,我们可以追踪到这么一段代码

    这不就是前面提到的通过声明Bean的方式来设置超时时间

    不同的是它加了@ConditionalOnMissingBean,这个注解就是说,一旦我们自己没有声明Options,就用他这个Options

    到这终于真像大白了。

    我们不设置超时时间,Spring就会给Feign.Builder加一个DEFAULT_OPTIONS这个Options

    在执行的时候,发现是DEFAULT_OPTIONS,说明我们没有主动设置过超是时间,就会使用Ribbon的超时时间。

    为了方便理清上面的逻辑,这里整一张图

    虽然Feign可以使用Ribbon的超时时间,但是Ribbon的配置的优先级是最最低的

    Feign or Ribbon配置用哪个好?

    其实我个人更倾向于使用Ribbon的配置方式。

    因为Ribbon除了可以设置超时时间之外,还可以配置重试机制、负载均衡等其它的配置

    为了简化和统一管理配置,使用Ribbon来配置超时时间。

    可能你会有疑问,Feign也支持重试机制,为什么不选择Feign?

    这是因为Feign重试机制没有Ribbon的好

    Ribbon重试的时候会换一个服务实例来重试,因为原来出错的可能不可用

    而Feign并不会换一个服务实例重试,他并不知道上一次使用的是哪个服务实例,这就导致可能会出现在一个不可用的服务实例上多次重试的情况。

    引入Hystrix时超时时间设置

    如果你之前的确没有研究过关于Feign超时时间的配置关系,那么此时你应该有所收获了。

    但是这就结束了么?

    不,事情没那么简单。

    如果你的项目中使用了Hystrix,那么就得小心前面说的那些配置了。

    由于Hystrix跟Feign毕竟是一家人,所以当引入Hystrix时,Feign就跟之前不一样了。

    Hystrix会去干一件事,那就是给每个Feign的http接口保护起来,毕竟Hystrix就是干保镖这个事的。

    但是这没保护还好,一保护问题就不自觉地出现了。

    Hystrix在保护的时候,一旦发现被保护的接口执行的时间超过Hystrix设置的最大时间,就直接进行降级操作。

    怎么降级的,这里咱不关心,咱关心的是这个Hystrix超时的最大值是多少。

    因为一旦这个时间小于Feign的超时时间,那么就会出现Http接口正在执行,也没有异常,仅仅是因为执行时间长,就被降级了。

    而Hystrix的默认的超时时间的最大值就只有1s。

    所以就算你Feign超时时间设置的再大,超过1s就算超时,然后被降级,太坑了。。

    所以我们需要修改这个默认的超时时间的最大值,具体的配置项如下

    hystrix:
      command:
        default:
          execution:
            isolation:
              thread:
                timeoutInMilliseconds: 30000

    并且时间上大致要符合下面这个原则

    重试次数我们前面也提到了,虽然一般我们不设置,但是为了严谨还是得加上,因为一次Http接口的执行时间肯定跟重试次数有关,重试次数越多,时间就越长。

    而连接超时时间 + 读超时时间设置方式,前面提到很多次,不论是通过Feign本身设置还是通过Ribbon来设置,都是可以的

    总结

    今天给大家扒了扒在不同使用条件下Feign的超时时间设置,总结起来大致如下:

    如果本篇文章对你所有帮助,欢迎转发、点赞、收藏、在看,非常感谢。

    往期热门文章推荐

    如何去阅读源码,我总结了18条心法

    如何写出漂亮代码,我总结了45个小技巧

    1.5万字+30张图盘点索引常见的11个知识点

    三万字盘点Spring/Boot的那些常用扩展点

    三万字盘点Spring 9大核心基础功能

    两万字盘点那些被玩烂了的设计模式

    扫码或者搜索关注公众号 三友的java日记 ,及时干货不错过,公众号致力于通过画图加上通俗易懂的语言讲解技术,让技术更加容易学习,回复 面试 即可获得一套面试真题。

    08-15 20:30