不过,默认情况下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,那么就可以通过声明Options
Bean和配置文件的方式更加方便地来设置超时时间
最终其实还是通过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
就算你不知道是什么也无所谓,你能看出的一件事就是,超时时间用的是传递给Client
的Options
参数
所以,综上,我们的问题就变得非常easy了,那就是什么时候
只有当这个条件成立时,才使用Ribbon的配置。
这里我们先来捋一捋前面提到的东西
前面我们反复提到,Client
的Options
最终只来自于两种配置
所以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的超时时间设置,总结起来大致如下:
如果本篇文章对你所有帮助,欢迎转发、点赞、收藏、在看,非常感谢。
往期热门文章推荐
扫码或者搜索关注公众号 三友的java日记 ,及时干货不错过,公众号致力于通过画图加上通俗易懂的语言讲解技术,让技术更加容易学习,回复 面试 即可获得一套面试真题。