前言
Spring Cloud 作为微服务解决方案 全家桶,集合了丰富的微服务组件,如Gateway、Feign、Hystrix,Ribbon、OkHttp、Eureka等等。而作为服务调用环节涉及到的几个组件:Feign、Hystrix,Ribbon、OkHttp 都有超时时间的设置,Spring Cloud 是如何优雅地把它们协调好呢?本文将为你揭晓答案。
1. Spring Cloud 中发起一个接口调用,经过了哪些环节?
Spring Cloud 在接口调用上,大致会经过如下几个组件配合:
Feign -----> Hystrix --->Ribbon ---> Http Client(apache http components 或者 Okhttp)
具体交互流程上,如下图所示:
Spring Cloud服务调用轨迹
接口化请求调用
当调用被@FeignClient注解修饰的接口时,在框架内部,会将请求转换成Feign的请求实例feign.Request,然后交由Feign框架处理。
Feign :转化请求
至于Feign的详细设计和实现原理,在此不做详细说明。
Hystrix :熔断处理机制
Feign的调用关系,会被Hystrix代理拦截,对每一个Feign调用请求,Hystrix都会将其包装成HystrixCommand,参与Hystrix的流控和熔断规则。如果请求判断需要熔断,则Hystrix直接熔断,抛出异常或者使用FallbackFactory返回熔断Fallback结果;如果通过,则将调用请求传递给Ribbon组件。
Ribbon :服务地址选择
当请求传递到Ribbon之后,Ribbon会根据自身维护的服务列表,根据服务的服务质量,如平均响应时间,Load等,结合特定的规则,从列表中挑选合适的服务实例,选择好机器之后,然后将机器实例的信息请求传递给Http Client客户端,HttpClient客户端来执行真正的Http接口调用;
HttpClient :Http客户端,真正执行Http调用
根据上层Ribbon传递过来的请求,已经指定了服务地址,则HttpClient开始执行真正的Http请求。
关于HttpClient的其中一个实现OkHttp的工作原理,请参考Spring Cloud OkHttp设计原理
2.每个组件阶段的超时设置
如上一章节展示的调用关系,每个组件自己有独立的接口调用超时设置参数,下面将按照从上到下的顺序梳理:
2.1 feign的默认配置
feign 的配置可以采用feign.client.config.....的格式为每个feign客户端配置,对于默认值,可以使用feign.client.config.default..的方式进行配置,该配置项在Spring Cloud中,使用FeignClientProperties类表示。
feign:
client:
config:
:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: full
errorDecoder: com.example.SimpleErrorDecoder
retryer: com.example.SimpleRetryer
requestInterceptors:
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
decode404: false
encoder: com.example.SimpleEncoder
decoder: com.example.SimpleDecoder
contract: com.example.SimpleContract
其中,关于feign的管理连接超时的配置项:
## 网络连接时间
feign.client.config..connectTimeout=
## 读超时时间
feign.client.config..readTimeout=
2.2 Spring Cloud 加载feign配置项的原理:
检查是否Feign是否制定了上述的配置项,即是否有FeignClientProperties实例;
如果有上述的配置项,则表明Feign是通过properties初始化的,即configureUsingProperties;
根据配置项feign.client.defaultToProperties的结果,使用不同的配置覆盖策略。
feign初始化的过程,其实就是构造Feign.Builder的过程,如下图所示:
2019-05-29_134522.png
相关代码实现如下:
protected void configureFeign(FeignContext context, Feign.Builder builder) {
FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);
if (properties != null) {
if (properties.isDefaultToProperties()) {
configureUsingConfiguration(context, builder);
configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
configureUsingP