一、线上问题
项目上线后,一段时间内运行都没有问题,突然运营人员说,某个接口一直失败。遂查看线上日志:
spring-cloud-openfeign 通过服务名调用的,服务发现没有找到可用服务实例?这是第一反应,应为没有看到目标 IP ,这个想法通过另外一个ping 接口调用被推翻了,于是怀疑是超时参数设置问题。
默认参数一定是有的,而且我的 OkhttpClient 对象已经设置了超时:
@Configuration
@ConditionalOnClass({Feign.class})
@AutoConfigureBefore({FeignAutoConfiguration.class})
public class OKHttpConfig {
@Value("${feign.okhttp.maxidleConnections}")
private Integer maxidleConnections;
@Value("${feign.okhttp.keepAliveDuration}")
private Integer keepAliveDuration;
public OKHttpConfig() {
}
@Bean
@LoadBalanced
public OkHttpClient okHttpClient() {
return (new OkHttpClient()).newBuilder().retryOnConnectionFailure(true).connectionPool(this.pool()).connectTimeout(100000L, TimeUnit.SECONDS).readTimeout(100000L, TimeUnit.SECONDS).writeTimeout(100000L, TimeUnit.SECONDS).build();
}
@Bean
public ConnectionPool pool() {
return new ConnectionPool(this.maxidleConnections, (long)this.keepAliveDuration, TimeUnit.MINUTES);
}
}
按照推测,明显这个没起作用,所以只能去看下具体调用时超时参数到底是怎么起作用的:
二、源码剖析
Ribbon 作为负载均衡调用 openFeign。会涉及到两个超时,一个是 Ribbon 的超时设置,一个是 openFeign 底层使用的 delegate 的超时。比如 httClient 或者 OkhttpClient。
下面看下这两个超时参数的设置方法:
# 注意这里对应的 org.springframework.cloud.openfeign.FeignClientProperties,并不是直接给 okhttp 使用的,会覆盖 okhttpClient 对象构造时设置的超时参数!
feign:
client:
config:
default:
connect-timeout: 20000
read-timeout: 20000
httpclient:
enabled: false
okhttp:
enabled: true
maxidleConnections: 150
keepAliveDuration: 10
ribbon:
ConnectTimeout: 5000
ReadTimeout: 10000
这两个参数同时设置或者只设置一个时到底会怎么作用呢,通过源码我们去了解下:
通过上面的代码可以看出,如果没有显式配置 configOverride,会使用 默认的 1s ,所以线上随着数据量的增加,接口一旦返回慢一点就会报超时错误!!
Ribbon 的默认配置在下面这里:
如果上面图看的有点晕,那下面这张图一定让你茅塞顿开:
总结一句话: Feign 集成了 Ribbon, 通过 LoadBalancerFeignClient 直接调用,而 LoadBalancerFeignClient 又通过代理 OkhttpClient (这里是feign包里的),继续往下代理,会调用真正的 okhttp3.OkHttpClient。
参数配置优先级:feign 没有配置会使用ribbon的,否则会使用 feign 配置的,如果feign 配置的和 okhttp3.OkHttpClient 参数不一致,会使用 feign 配置的!