Ribbon的超时配置会覆盖OpenFeign的超时配置吗

2023年 7月 14日 20.0k 0

前言

平常的开发工作中调用Rpc服务最关注的性能指标就是响应时间rt,OpenFeign提供了超时时间配置项。本文将从源码层面分析OpenFeign超时时间配置原理,以及Ribbon超时配置的关系分析。

通过本文可以明白两个问题:

  • OpenFeign超时配置和Ribbon超时配置的关系
  • OpenFeign超时配置能否动态实时修改生效

OpenFeign配置超时方式

在OpenFeign中,有一个超时项配置类,专门用来接收超时配置的。
支持连接超时和读数据超时配置,所有的超时配置最终都会生成对应的Options实例。

   public static class Options {
        //连接超时时间
        private final int connectTimeoutMillis;
        //读数据超时时间
        private final int readTimeoutMillis;
        private final boolean followRedirects;
   }  

默认多久超时呢?
FeignRibbonClientAutoConfiguration自动配置中使用了默认的构造函数,根据构造函数参数,默认连接超时10s,读数据6s超时,也就是不改配置,OpenFeign的接口默认超时时间有10s。

 public Options() {
            this(10000, 60000);
        }

OpenFeign到底有哪些配置方式呢?

  • 全局配置方式一

feign.client.config.default.connectTimeout=1000 feign.client.config.default.readTimeout=1000

  • 全局配置方式二
@Configuration
public class TestFeignClientConfiguration {
    
    @Bean
    public Request.Options options() {
        //配置全局300ms就超时
        Request.Options options = new Request.Options(300, 300);
        return new Request.Options();
    }
}
}
  • 指定FeignClient生效配置一
    以feignClientFeignClientApi为例
    feign.client.config.FeignClientApi.connectTimeout=1000 feign.client.config.FeignClientApi.readTimeout=1000

  • 指定FeignClient生效配置二

@FeignClient(value = "fox-server", contextId = "feignClientApi", configuration = TestFeignClientConfiguration.class)
public interface FeignClientApi {
        @PostMapping("/get")
        String getName(@RequestBody @Validated DemoRequest request);
}

//和全局配置比少了Configuration注解
public class TestFeignClientConfiguration {
    
    @Bean
    public Request.Options options() {
        //配置全局300ms就超时
        Request.Options options = new Request.Options(300, 300);
        return new Request.Options();
    }
}
}

配置生效原理

这些配置是怎么生效的呢?

首先在Feign的自动装配类FeignAutoConfiguration中,启用了
FeignClientProperties配置类。这样配置文件里的属性就可以被解析了。

@Configuration
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class,
        FeignHttpClientProperties.class })
public class FeignAutoConfiguration {
}

配置类以feign.client开头

@ConfigurationProperties("feign.client")
public class FeignClientProperties {

    private boolean defaultToProperties = true;

    private String defaultConfig = "default";

    private Map config = new HashMap();

FeignClientConfiguration中有超时时间字段

image.png

这样Spring容器启动后会将feign.client为前缀的配置项注入到FeignClientProperties类对象中。

由于配置项config是一个map,只要key是对应的FeignClient里的contextId或者default就会生效。

具体在哪使用的呢?

在Feign初始化阶段,从Spring容器读取FeignClientProperties

protected void configureFeign(FeignContext context, Feign.Builder builder) {
        //查找`FeignClientProperties`
        FeignClientProperties properties = this.applicationContext
                .getBean(FeignClientProperties.class);
        if (properties != null) {
            if (properties.isDefaultToProperties()) {
                configureUsingConfiguration(context, builder);
                configureUsingProperties(
                        properties.getConfig().get(properties.getDefaultConfig()),
                        builder);
                configureUsingProperties(properties.getConfig().get(this.contextId),
                        builder);
            }
            else {
                configureUsingProperties(
                        properties.getConfig().get(properties.getDefaultConfig()),
                        builder);
                configureUsingProperties(properties.getConfig().get(this.contextId),
                        builder);
                configureUsingConfiguration(context, builder);
            }
        }
        else {
            configureUsingConfiguration(context, builder);
        }
    }

上面的代码就涉及到了配置优先级问题。默认是配置文件里的优先。

image.png

根据优先级确定配置后,构造Request.Options对象,与FeignClient对象绑定。

protected void configureUsingProperties(
            FeignClientProperties.FeignClientConfiguration config,
            Feign.Builder builder) {
        
        if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
            builder.options(new Request.Options(config.getConnectTimeout(),
                    config.getReadTimeout()));
        }
    
    }

上面是初始化好了Feign的超时配置,如果使用了ribbon负载均衡,在执行阶段可能会被修改。

  • 如果是OpenFeign默认配置,使用ribbon配置覆盖默认配置

org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient#getClientConfig

IClientConfig getClientConfig(Request.Options options, String clientName) {
        IClientConfig requestConfig;
        //默认10s的配置情况,使用ribbon配置覆盖默认配置
        if (options == DEFAULT_OPTIONS) {
            requestConfig = this.clientFactory.getClientConfig(clientName);
        }
        else {
            requestConfig = new FeignOptionsClientConfig(options);
        }
        return requestConfig;
    }

执行请求前,如果有配置优先使用已有的配置,兜底使用ribbon配置。整体流程如下

image.png

@Override
    public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
            throws IOException {
        Request.Options options;
        //存在配置了,读已经配置的,如果为空,使用ribbon配置
        if (configOverride != null) {
            RibbonProperties override = RibbonProperties.from(configOverride);
            options = new Request.Options(override.connectTimeout(this.connectTimeout),
                    override.readTimeout(this.readTimeout));
        }
        else {
            options = new Request.Options(this.connectTimeout, this.readTimeout);
        }
        Response response = request.client().execute(request.toRequest(), options);
        return new RibbonResponse(request.getUri(), response);
    }

OpenFeign超时配置问题

配置都是生成FeignClient对象之前就设置好了,如果想动态实时生效是不支持的,修改超时时间需要重启服务。有没有办法不重启服务实时生效呢?

我们可以写一个aop切面,拦截feign.Client#execute方法,第二个参数就是调用时候会使用的Options参数,只要修改第二个参数就可以了。

image.png

总结

1、OpenFeign支持配置类和配置文件配置超时时间,默认连接超时时间是10s,读数据时间6s,配置文件的优先生效
2、Ribbon支持配置超时时间,默认1s,如果OpenFeign没有单独配置超时时间,则会使用Ribbon的超时时间覆盖
3、OpenFeign超时时间是构造Client之前就初始化好了,不支持动态修改生效,可以通过aop拦截Client的execute方法修改。

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论