Feign超时时间设置
Feign超时时间设置起因feign超时时间配置发现了两个问题问题一排查首先看入口类FeignAutoConfiguration配置文件对应类FeignClientPropertiesFeignClientFactoryBean类用于读取配置参数来进行feign配置问题二排查LoadBalancerFeignClientSpringClientFactory起因最近发现登录功能有时会报timeou
Feign超时时间设置
起因
最近想通过配置feign参数,来改变feign客户端之间connectTimeout和readTimeout。
feign参数配置
feign:
client:
config:
default:
#不设置connectTimeout会导致readTimeout设置不生效
connectTimeout: 3000
readTimeout: 6000
发现了两个问题
1 只设置单独的connectTimeout或者readTimeout时不生效,必须两个值都设置才行
2 发现feign默认的connectTimeout时长是10s,readTimeout时长是60s。但是实际服务之间调用readTimeout超过1秒就会超时。
问题一排查
首先看入口类FeignAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class,
FeignHttpClientProperties.class })
@Import(DefaultGzipDecoderConfiguration.class)
public class FeignAutoConfiguration {
配置文件对应类FeignClientProperties
@ConfigurationProperties("feign.client")
public class FeignClientProperties {
排查发现feign是通过FeignClientProperties来接收配置文件中的配置的。
FeignClientFactoryBean类用于读取配置参数来进行feign配置
protected void configureUsingProperties(
FeignClientProperties.FeignClientConfiguration config,
Feign.Builder builder) {
if (config == null) {
return;
}
if (config.getLoggerLevel() != null) {
builder.logLevel(config.getLoggerLevel());
}
if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
builder.options(new Request.Options(config.getConnectTimeout(),
config.getReadTimeout()));
}
if (config.getRetryer() != null) {
Retryer retryer = getOrInstantiate(config.getRetryer());
builder.retryer(retryer);
}
if (config.getErrorDecoder() != null) {
ErrorDecoder errorDecoder = getOrInstantiate(config.getErrorDecoder());
builder.errorDecoder(errorDecoder);
}
跟踪configureUsingProperties方法最终发现了问题一产生的原因。
问题二排查
基于问题一中configureUsingProperties方法去跟踪Request.Options的默认值发现如下
/**
* Creates the new Options instance using the following defaults:
* <ul>
* <li>Connect Timeout: 10 seconds</li>
* <li>Read Timeout: 60 seconds</li>
* <li>Follow all 3xx redirects</li>
* </ul>
*/
public Options() {
this(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true);
}
/**
* Creates a new Options instance.
*
* @param connectTimeoutMillis connection timeout in milliseconds.
* @param readTimeoutMillis read timeout in milliseconds.
* @param followRedirects if the request should follow 3xx redirections.
*
* @deprecated please use {@link #Options(long, TimeUnit, long, TimeUnit, boolean)}
*/
@Deprecated
public Options(int connectTimeoutMillis, int readTimeoutMillis, boolean followRedirects) {
this(connectTimeoutMillis, TimeUnit.MILLISECONDS,
readTimeoutMillis, TimeUnit.MILLISECONDS,
followRedirects);
}
其默认的connectTimeout时长是10s,readTimeout时长是60s。很奇怪为啥我的项目1秒就超时呢??
LoadBalancerFeignClient
该类用于处理feign客户端的http请求,跟断点进来的。具体咋找到这这里我也记不清了,总之很曲折。
@Override
public Response execute(Request request, Request.Options options) throws IOException {
try {
URI asUri = URI.create(request.url());
String clientName = asUri.getHost();
URI uriWithoutHost = cleanUrl(request.url(), clientName);
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
this.delegate, request, uriWithoutHost);
IClientConfig requestConfig = getClientConfig(options, clientName);
return lbClient(clientName)
.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
}
catch (ClientException e) {
IOException io = findIOException(e);
if (io != null) {
throw io;
}
throw new RuntimeException(e);
}
}
//获取feign的方法继续跟这里到【this.clientFactory.getClientConfig】
IClientConfig getClientConfig(Request.Options options, String clientName) {
IClientConfig requestConfig;
if (options == DEFAULT_OPTIONS) {
requestConfig = this.clientFactory.getClientConfig(clientName);
}
else {
requestConfig = new FeignOptionsClientConfig(options);
}
return requestConfig;
}
可以看到当options没有被修改过时,feign的配置信息会通过clientFactory(SpringClientFactory)获取。这里可以自己打个断点,可以看到clientFactory.getClientConfig(xxx)返回的配置信息中connectTimeout和readTimeout都只有1秒,到此feign的两个问题已经找到了答案。
SpringClientFactory
延申下clientFactory对象其实就是SpringClientFactory,该类作用:创建客户端、负载均衡器和客户端配置实例的工厂。 它为每个客户端名称创建一个 Spring ApplicationContext,并从那里提取它需要的 bean。个人理解就是每个feign客户端在服务端中都有一个独立的子容器。这块还有待学习。
/**
* Get the client config associated with the name.
* @param name name to search by
* @return {@link IClientConfig} instance
* @throws RuntimeException if any error occurs
*/
public IClientConfig getClientConfig(String name) {
return getInstance(name, IClientConfig.class);
}
更多推荐
所有评论(0)