Spring Cloud Open Feign 超时配置及源码分析

前言:

在开发 Spring Cloud 微服务项目时候,Feign 调用是非常常见的,Feign 调用的底层还是 HTTP 的远程调用,会有超时问题,如果没有搞清楚超时问题,生产环境的调用肯那个会有种种问题出现,本篇我们来分享一下 Feign 调用的超时配置。

连接超时和读取超时配置

  • ConnectTimeout(连接超时):Feign 是基于 HTTP 的远程调用,众所周知,HTTP 调用会进行 TCP 的三次握手,连接超时时间,就是多少秒没连接上,就会抛出超时异常,Feign 连接超时默认 10 秒。
  • ReadTimeout(读取超时):HTTP 成功连接后,客户端发会送请求报文,服务端收到后解析并返回响应报文,在写出响应报文时,如果超过了设置的时间还没写完,就会抛出读取超时异常,在某些接口请求数据量大的时候,就很容易出现读取超时,Feign 读取超时默认 60 秒。

超时演示

我们在被 Feign 调用的接口中让线程 sleep 61 秒,调用者服务就抛出超时异常了。

在这里插入图片描述

Feign 默认超时时间源码

通过源码 debugger 我们可以知道 Feign 的默认读取超时时间是 60 秒,默认连接超时时间是 10 秒。

在这里插入图片描述

自定义 Feign 超时时间

Feign 支持在 ribbon 或者 feign 配置项下配置超时时间,feign 下配置优先级最高,但是新版 Spring Cloud 已经移除了 ribbon,因此建议配置在 feign中,如下配置:

#默认连接超时时间
feign.client.config.default.connect-timeout=5000
#默认读取超时时间
feign.client.config.default.read-timeout=3000

debugger 如下图,Feign 超时配置已经生效,default 表示作用于所有客户端,也可替换 default 为具体的客户端名称,表示作用于单个客户端,可以给每个客户端配置不同的超时时间。

在这里插入图片描述

给指定的服务端配置超时时间如下:

#默认连接超时时间
feign.client.config.order-service.connect-timeout=4000
#默认读取超时时间
feign.client.config.order-service.read-timeout=4000

debugger 验证如下:

在这里插入图片描述

Feign 源码分析

Feign 参数的初始化

Feign 通过接口生成代理对象,扫描到 Feign 接口构建代理对象,在 Feign#builder 创建构建者时,Feign 客户端相关的参数都是在这个时候初始化的,超时时间也是在这个时候初始化的,Feign#builder 会创建一个 Options 对象,源码如下:

//feign.Feign.Builder#Builder
public Builder() {
   
	//日志级别
	this.logLevel = Level.NONE;
	this.contract = new Default();
	//客户端
	this.client = new feign.Client.Default((SSLSocketFactory)null, (HostnameVerifier)null);
	//重试器
	this.retryer = new feign.Retryer.Default();
	//日志
	this.logger = new NoOpLogger();
	//编码器
	this.encoder = new feign.codec.Encoder.Default();
	//解码器
	this.decoder = new feign.codec.Decoder.Default();
	this.queryMapEncoder = new FieldQueryMapEncoder();
	//错误解码器
	this.errorDecoder = new feign.codec.ErrorDecoder.Default();
	//超时配置
	this.options = new Options();
	//处理器工厂
	this.invocationHandlerFactory = new feign.InvocationHandlerFactory.Default();
	this.closeAfterDecode = true;
	//传播策略
	this.propagationPolicy = ExceptionPropagationPolicy.NONE;
	//是否强制解码
	this.forceDecoding = false;
	this.capabilities = new ArrayList();
}

前面我们说 Feign 的默认连接超时时间是 10 秒,默认读取超时时间是 60 秒,我们来从源码证明,Options 构造方法源码如下:

//feign.Request.Options#Options()
public Options() {
   
	this(10L, TimeUnit.SECONDS, 60L, TimeUnit.SECONDS, true);
}

//feign.Request.Options#Options(long, java.util.concurrent.TimeUnit, long, java.util.concurrent.TimeUnit, boolean)
public Options(long connectTimeout, TimeUnit connectTimeoutUnit, long readTimeout, TimeUnit readTimeoutUnit, boolean followRedirects) {
   
	this.connectTimeout = connectTimeout;
	this.connectTimeoutUnit = connectTimeoutUnit;
	this.readTimeout = readTimeout;
	this.readTimeoutUnit = readTimeoutUnit;
	this.followRedirects = followRedirects;
}

从 Options 构造方法源码中可以证明 Feign 的默认超时时间。

SentinelFeign.Builder#build 方法源码分析

SentinelFeign.Builder#build 方法主要逻辑如下:

  • 获取应用程序上下文。
  • 通过应用程序上下文获取到 target 对象的 BeanDefinition 。
  • 通过 BeanDefinition 获取到 FeignClientFactoryBean 。
  • 获取 FallBack 类和工厂。
  • 创建代理对象的处理器 SentinelInvocationHandler(项目中引入了 Sentinel)。
//com.alibaba.cloud.sentinel.feign.SentinelFeign.Builder#build
public Feign build() {
   
	super.invocationHandlerFactory(new InvocationHandlerFactory() {
   
		public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
   
			//target 就是我们定义的 feign 接口 例如 OrderFeign
			//dispatch 就是我们的接口方法  例如  com.user.service.feign.OrderFeign.queryOrder()
			//通用应用程序上下文
			GenericApplicationContext gctx = (GenericApplicationContext)Builder.this.applicationContext;
			//target 对象的 beanDefinition 对象  
			BeanDefinition def = gctx.getBeanDefinition(target.type().getName());
			//FeignClient bean 工厂
			FeignClientFactoryBean feignClientFactoryBean = (FeignClientFactoryBean)def.getAttribute("feignClientsRegistrarFactoryBean");
			//Feign 接口中配置的的 fallback 
			Class fallback = feignClientFactoryBean.getFallback();
			//fallback 工厂
			Class fallbackFactory = feignClientFactoryBean.getFallbackFactory();
			
### Spring Cloud Feign 微服务间通信的使用方法及原理 #### 使用方法 在Spring Cloud中,Feign被设计为一种声明式的HTTP客户端工具,用于简化微服务间的通信过程。以下是其主要使用方式: 1. **引入依赖** 需要在项目的`pom.xml`文件中添加Spring Cloud Open Feign的相关依赖[^2]。 ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> ``` 2. **启用Feign功能** 在启动类上添加`@EnableFeignClients`注解来开启Feign的功能支持[^3]。 ```java @SpringBootApplication @EnableFeignClients public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 3. **定义接口** 创建一个接口,并使用`@FeignClient`注解指定目标服务名称以及请求路径[^1]。 ```java @FeignClient(name = "service-name", url = "http://localhost:8080") public interface ExampleServiceClient { @GetMapping("/example/{id}") String getExampleById(@PathVariable("id") Long id); @PostMapping("/example") ResponseEntity<String> createExample(@RequestBody ExampleRequest request); } ``` 4. **调用远程服务** 在业务逻辑层注入上述定义好的Feign Client接口实例,像调用本地方法一样发起对其他微服务的RESTful API请求。 #### 原理分析 Feign的核心机制在于它是一个动态代理框架,在运行时会根据配置生成具体的实现类完成实际网络交互操作。具体来说有以下几个方面特点及其背后的工作流程: 1. **声明式编程模型** 用户只需编写简单的Java接口加上必要的注解描述要访问的服务端点信息(URL模板参数化表达),无需关心底层细节如连接管理、序列化反序列化等工作项均自动处理完毕。 2. **集成Ribbon实现负载均衡** 默认情况下,当指定了某个Eureka注册中心内的服务名作为目标地址时,Feign内部便会借助于嵌入其中的 Ribbon 组件随机挑选可用节点执行最终的数据交换动作从而达到分布式环境下的高可靠性需求。 3. **Hystrix熔断保护机制可选加入** 如果项目还额外集成了 Hystrix,则可以进一步增强系统的容错能力,防止因单个下游失败而引发连锁反应影响整体稳定性。 ```java // 开启Hystrix支持后的例子 @FeignClient(name="product-service", fallback=ProductFallback.class) public interface ProductServiceApi{ ... } ``` ### 注意事项 为了提高性能表现还可以考虑一些常见的优化手段比如设置合理的超时时间范围或者自定义日志级别以便调试排查问题等等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值