OpenFeign源码分析

文章围绕 OpenFeign 3.0.6 展开,阐述其主要配置类、超时配置、Http 客户端、重试处理、负载均衡等内容。
OpenFeign源码分析

环境说明

框架
版本
OpenFeign
3.0.6
loadbalancer
3.0.5
Spring Cloud
3.0.5

主要配置

在 FeignAutoConfiguration 中导入了三个配置类
notion image
这里最常用的是 FeignClientProperties 与 FeignHttpClientProperties 两个。 FeignClientProperties 是默认的配置类,而 FeignHttpClientProperties 则是针对使用 ApacheHttpClient 与 OkHttpClient 时提供的配置类。

FeignClientProperties

FeignClient 有一个默认的配置,这个配置是针对全局的,即:

yaml

feign: # Feign客户端配置 client: config: # Feign客户端默认配置 default: # 连接超时 connectTimeout: ${FEIGN_CLIENT_CONFIG_DEFAULT_CONNECT_TIMEOUT:30000} # 读取超时 readTimeout: ${FEIGN_CLIENT_CONFIG_DEFAULT_READ_TIMEOUT:300000} # Feign日志级别:NONE/BASIC/HEADERS/FULL loggerLevel: ${FEIGN_CLIENT_CONFIG_DEFAULT_LOGGER_LEVEL:FULL}
YAML
也可以指定 Feign 客户端进行配置:

yaml

feign: client: config: spucClient: connectTimeout: ${FEIGN_CLIENT_CONFIG_DEFAULT_CONNECT_TIMEOUT:30000} readTimeout: ${FEIGN_CLIENT_CONFIG_DEFAULT_READ_TIMEOUT:300000} loggerLevel: ${FEIGN_CLIENT_CONFIG_DEFAULT_LOGGER_LEVEL:FULL}
YAML

超时配置

Feign 的超时问题可能是我们经常会遇到的,我们可以通过 FeignClientProperties 默认配置类去指定全局的连接超时与读取超时时间,如果我们没有去配置则 Feign 会使用一个默认的配置时间。 Feign 的构造内部类 Feign.Builder 中有一个 Client 类,进行 Feign 调用是执行 Client 的 execute 方法,方法参数中有个 Options 类,这个就是 Feign 配置超时时间的地方。进入类内部可以看到它有个构造函数默认设置了 连接超时 10s,读取超时 60s

java

public Options() { this(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true); }
Java
在 3.0.x 版本之前,Feign 的超时时间配置不会使用自己 Options 类配置的时间,而是会使用 Ribbon 中配置的时间,在低版本中可以看下 execute 方法的实现类 LoadBalancerFeignClient 可以看到,如果 Options 是默认的,则会从 Ribbon 获取配置,如果不是才会使用 Feign Options 的配置
notion image
但是在 3.0.x 版本之后,没有这个判断了,如果没有通过 FeignClientProperties 配置超时时间,则会直接使用 Feign Options 类配置的超时时间,Ribbon 配置的超时时间不会生效。

Http 客户端

OpenFeign 默认是使用 Java 的 HttpURLConnection 作为 Http 请求客户端的。 在 Feign.Client 类中有一个默认的实现类,其中 execute 方法中则是具体发起请求的逻辑:

java

@Override public Response execute(Request request, Options options) throws IOException { HttpURLConnection connection = convertAndSend(request, options); return convertResponse(connection, request); }
Java
在 convertAndSend 方法中通过 HttpURLConnection 构建 Http 请求处理
notion image

重试处理

Feign 本身是默认不开启重试的
notion image
在之前的低版本中,可以通过 Ribbon 配置是否重试

yaml

ribbon: # 客户端读取超时时间,超时时间要小于Hystrix的超时时间,否则重试机制就无意义了 ReadTimeout: ${RIBBON_READ_TIMEOUT:10000} # 客户端连接超时时间 ConnectTimeout: ${RIBBON_CONNECT_TIMEOUT:3000} # 访问实例失败(超时),允许自动重试,设置重试次数,失败后会更换实例访问,请一定确保接口的幂等性,否则重试可能导致数据异常。 OkToRetryOnAllOperations: true MaxAutoRetries: 1 MaxAutoRetriesNextServer: 1
YAML
但在高版本中,在配置客户端时会默认使用可重试的客户端

java

@Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(LoadBalancerProperties.class) class DefaultFeignLoadBalancerConfiguration { @Bean @ConditionalOnMissingBean @Conditional(OnRetryNotEnabledCondition.class) public Client feignClient(LoadBalancerClient loadBalancerClient, LoadBalancerProperties properties, LoadBalancerClientFactory loadBalancerClientFactory) { return new FeignBlockingLoadBalancerClient(new Client.Default(null, null), loadBalancerClient, properties, loadBalancerClientFactory); } @Bean @ConditionalOnMissingBean @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate") @ConditionalOnBean(LoadBalancedRetryFactory.class) @ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true", matchIfMissing = true) public Client feignRetryClient(LoadBalancerClient loadBalancerClient, LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerProperties properties, LoadBalancerClientFactory loadBalancerClientFactory) { return new RetryableFeignBlockingLoadBalancerClient(new Client.Default(null, null), loadBalancerClient, loadBalancedRetryFactory, properties, loadBalancerClientFactory); } }
Java
LoadBalancerProperties 配置类中的 Retry 内部类中默认是启用重试的,因此会启用 RetryableFeignBlockingLoadBalancerClient 这个 Client 实现类
notion image

重试配置

LoadBalancerProperties.Retry 类的主要配置如下:

yaml

spring: cloud: loadbalancer: retry: # 是否重试 enabled: ${SPRING_CLOUD_LB_RETRY_ENABLED:true} # 重试同一个实例的次数 max-retries-on-same-service-instance: ${SPRING_CLOUD_LB_RETRY_SAME:0} # 重试下一个实例的次数 max-retries-on-next-service-instance: ${SPRING_CLOUD_LB_RETRY_NEXT:1}
YAML
RetryableFeignBlockingLoadBalancerClientexecute方法中会创建重试策略,具体内容就是按我们以上的配置来
notion image

负载均衡

Ribbon(已弃用)

Ribbon 是Netflix发布的负载均衡器,它有助于控制HTTP和TCP客户端的行为。在低版本的 Open Feign 中默认是使用 Ribbon 作为负载均衡器来使用的。 但是由于Ribbon已经进入维护模式,并且Ribbon 2并不与Ribbon 1相互兼容,目前已弃用。

Spring Cloud Loadbalancer

Spring Cloud 从 2020 版本开始弃用 Ribbon,官方建议使用 Spring Cloud Loadbalancer 来代替 Ribbon,因此负载均衡默认组件也由 Ribbon 变成 Loadbalancer。
OpenFeign 在选择 RetryableFeignBlockingLoadBalancerClient重试客户端后,主要的执行逻辑在这个实现类的 execute 方法中。 上面有提到它有创建重试策略的逻辑,接下来会通过Loadbalancer的负载均衡策略选择一个实例,最后再发起调用请求。 选择实例主要是loadBalancerClient.choose(serviceId, lbRequest) 这个方法实现的,我们可以看下它里面的具体逻辑:

java

public <T> ServiceInstance choose(String serviceId, Request<T> request) { //首先获取负载均衡器 ReactiveLoadBalancer<ServiceInstance> loadBalancer = this.loadBalancerClientFactory.getInstance(serviceId); if (loadBalancer == null) { return null; } else { //通过loadBalancer.choose的实现类获取ServiceInstance Response<ServiceInstance> loadBalancerResponse = (Response)Mono.from(loadBalancer.choose(request)).block(); return loadBalancerResponse == null ? null : (ServiceInstance)loadBalancerResponse.getServer(); } }
Java
loadBalancer.choose()方法有多个实现类,分别是不同的负载均衡策略,我们也可以自己实现
notion image
默认配置的策略是轮询
notion image
上一篇
偶发性出现Connection is closed异常排查
下一篇
Feign调用超时时间配置不生效问题排查
Loading...
2024-11-11
最新发布
Spring事务资源解绑异常问题
2025-3-5
智能IDE与插件集成DeepSeek指南:开发者的高效编程新选择
2025-3-5
Account Note:一款解决网站账号管理烦恼的浏览器扩展
2025-3-5
ChatGPT与豆包的图像生成
2024-11-12
Windows10家庭版安装Docker记录
2024-11-12
Mybatis Log Parser插件
2024-11-11