Java小案例-Feign的超时时间如何设置

前言

Feign的超时时间如何设置?

Feign的超时时间设置方式并不固定,它取决于Feign在项目中是如何使用的,不同的使用方式,超时时间设置方式也不大相同,甚至还可能有坑。

由于文章会涉及到Feign的底层知识,如果不懂点Feign的基本概念的话,后面就看不下去

所以为了方便不了解Feign的小伙伴也能够读得懂文章,这里我就简单地说说Feign的原理,点到为止,虽然不深入,但足够应付这篇文章了

Feign的作用

在项目中,我们经常需要调用第三方提供的Http接口,此时我们就可以使用一些Http框架来实现,比如HttpClient

public class HttpClientDemo {public static void main(String[] args) throws Exception {//创建一个HttpClientHttpClient httpClient = HttpClientBuilder.create().build();//构建一个get请求HttpGet httpGet = new HttpGet("http://192.168.100.1:8080/order/1");//发送请求,获取响应HttpResponse httpResponse = httpClient.execute(httpGet);HttpEntity httpEntity = httpResponse.getEntity();//读出响应值String response = EntityUtils.toString(httpEntity);System.out.println("Response: " + response);}}

如果项目中只有一两个这种第三方接口这样写还行,但是一旦这种三方接口过多的话,每次都得这样组装参数,发送请求,写一堆同样的代码,就显然很麻烦了。

所以为了简化发送Http请求的开发,减少重复代码,Feign就出现了。

Feign是一个声明式的Http框架

当你需要调用Http接口时,你需要声明一个接口,加一些注解就可以了

而像组装参数、发送Http请求等重复性的工作都交给Feign来完成。

Feign的原理

虽然有了接口,但是仅仅有接口是不够的,因为接口又不能创建对象,我们得需要对象。

Feign为了方便我们为接口创建对象,提供的Feign.Builder这个内部类

这个类的作用就是解析接口的上的注解,为接口生成一个动态代理对象,后面通过这个代理对象就可以发送请求了。

这个内部类有很多属性,这些属性都是Feign的核心组件。

在这些核心的组件中有一个叫Client的,上图中我圈出来了。

这个Client类划个重点,非常非常重要,本文讨论的东西跟他有密切关系。

它只有一个方法Response execute(Request request, Options options)

方法的第一个参数Request就是封装了http请求的url、请求方法,请求头、请求体之类的参数

第二个参数Options就是本文的主题,封装了超时时间。

返回值Response就是封装了一些响应码status、响应头之类的

所以通过方法的参数和返回值也可以猜出来,这个Client作用是用来组装Http请求参数,发送Http请求的

并且http请求超时时间是根据传给ClientOptions参数来决定的

如果想更深一步了解Feign原理,可在公众号菜单栏springcloud分类中查看

Feign单独使用时超时时间设置

Feign本身就是一个http客户端,可独立使用,Feign提供了两种超时时间设置方式

1、通过Feign.Builder设置

前面提到,Feign.Builder的作用是为接口的动态代理对象的

Feign.Builder里面有很多属性,其中就有关于超时时间的属性Options

如果你不设置,那么超时时间就是默认的

默认的就是连接超时10s,读超时60s

所以可以通过设置Feign.Builder中的options来设置超时时间

来个demo

环境准备,就是一个简单的SpringBoot项目,引入一个Feign的依赖

<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId><version>2.2.5.RELEASE</version></dependency>
</dependencies>

声明接口 + 注解

public interface UserApi {@RequestLine("GET /user/{userId}")User queryUser(@Param("userId") Integer userId);}

这里演示的是Feign原生的使用方式,脱离于SpringCloud环境,所以Spring的那些@GetMappring就不支持了,改用Feign本身提供的注解

测试代码

public class FeignDemo {public static void main(String[] args) {UserApi client = Feign.builder()//设置连接和读超时间都是5s.options(new Request.Options(5, TimeUnit.SECONDS, 5, TimeUnit.SECONDS, true)).target(UserApi.class, "http://localhost:8088");User user = client.queryUser(123);}}

这里面的请求路径都是不存在的,因为我们只关心传给ClientOptions参数值

Client在我们不设置的时候,就用默认的实现Client.Default

断点打到execute方法的实现,运行,走起

结果就是我们设置的5s

2、在接口方法参数设置

除了在通过Feign.Builder时设置之外,Feign还支持在接口的方法参数上设置

此时你只需要在接口的方法上加一个Options类型的参数

@RequestLine("GET /user/{userId}")
User queryUser(@Param("userId") Integer userId, Request.Options options);

这样在传参数时就可以设置超时时间了

User user = client.queryUser(123, new Request.Options(3, TimeUnit.SECONDS, 3, TimeUnit.SECONDS, true));

同样地,debug就可以看见我们设置的3s了

这两种设置超时时间的主要区别就是方法参数设置超时时间的优先级高于Feign.Builder设置的超时时间

用一张图来总结一下上面的关系

所以,如果你单独使用Feign的时候,你就可以通过如上的两种方式来设置超时时间。

SpringCloud下Feign单独使用超时时间设置

在SpringCloud环境下,只是对Feign进行了一层包装,所以即使没有Ribbon和注册中心,Feign也是可以单独使用的,但是用法有点变化

  • 注解都换成SpringMVC的注解

  • 接口上需要加@FeignClient注解

  • 用@EnableFeignClients扫描这些接口

不过,默认情况下Feign还是需要结合Ribbon来使用的

如果你只想单独使用Feign,那么就设置一下@FeignClient注解的url属性,指定请求的地址和端口就可以了

所以,既然只是包装,前面提到的两种方式设置超时时间当然可以继续使用:

  • 通过Feign.Builder

  • 通过接口的方法参数

方法参数设置形式跟前面提到的一模一样,但是通过Feign.Builder来设置却不太一样

由于SpringCloud会自己创建Feign.Builder,不需要我们创建,所以在设置Options时,Spring提供了两种快捷方式来设置

不过最终还是设置到Feign.Builder中

1、声明一个Options Bean

Spring在构建Feign.Builder的时,会从容器中查找Options这个Bean,然后设置到Feign.Builder

@Configuration
public class FeignConfiguration {@Beanpublic Request.Options options() {return new Request.Options(8, TimeUnit.SECONDS, 8, TimeUnit.SECONDS, true);}}

此时debug就可以看到设置到Feign.Builder的代码

这段代码在FeignClientFactoryBean中的configureUsingConfiguration方法中

2、配置文件中设置

除了声明Bean之外,Spring还提供了通过配置文件的方式配置,如下:

feign:client:config:default:connectTimeout: 10000readTimeout: 10000

同样地,debug就可以看见

这段代码在FeignClientFactoryBean中的configureUsingConfiguration方法中

声明Bean和配置文件都可以设置,那么同时设置哪种优先级高呢?

无特殊配置,遵守SpringBoot本身的配置规定

约定 > 配置 > 编码

所以基于这个规定,配置文件的配置优先级大于手动声明Bean的优先级。

到这,我们又学到了两种Spring为了方便我们设置Feign.Builder提供的配置方式:

  • 声明Options Bean

  • 配置文件

把他们俩加到前面画的图中

所以,如果你使用了SpringCloud提供的方式来使用Feign,那么就可以通过声明OptionsBean和配置文件的方式更加方便地来设置超时时间

最终其实还是通过Feign.Builder来设置的

SpringCloud下通过Ribbon来设置

当Feign配合Ribbon使用时,除了上面两种方式之外,还可以通过Ribbon来设置超时时间。

但是这里我不知道你会不会好奇

Ribbon不是负载均衡组件,怎么可以设置超时时间?

其实这跟Ribbon的定位有关,除了负载均衡组件之外,Ribbon也干发送Http请求的事,也就是不配合Feign,他照样可以发送http请求。

来个简单demo

解释一下上面的代码意思

  • 第一步,设置user服务的两个服务实例地址

  • 第二步,获取user服务对应的RestClient,这RestClient就可以用来发送http请求

  • 第三步,构建一个http请求

  • 第四步,就是发送http请求,以负载均衡的方式

这样,此时就会从两个服务实例中根据负载均衡选取一个服务地址发送http请求,

Ribbon既然可以发送Http请求,那么自然而然就可以设置超时时间

Feign在整合Ribbon的时候,为了统一配置,就默认将自己的超时时间交由Ribbon管理

所以,在默认情况下,Feign的超时时间可以由Ribbon配置

而Ribbon默认连接和读超时时间只有1s,所以在默认情况下,Feign的超时时间只有1s。

IClientConfig是Ribbon的配置类,Ribbon所有的配置都可以从IClientConfig中获取。

所以,在默认情况下,很容易就发生超时,不过我们可以通过配置文件修改即可

ribbon:ConnectTimeout: 5000ReadTimeout: 5000

你知道你发现没,上面说通过Ribbon设置Feign的超时时间,一直提到前面一直提到这个词

默认

什么情况下叫默认呢?

所谓的默认,就是当你不主动设置Feign的超时时间的时候,就是默认。

换句话说,一旦你通过上面说的那些配置方式设置Feign的超时时间,就不是默认了

此时通过Ribbon设置的超时时间就不会生效了

Feign是如何在默认情况下将超时时间交给Ribbon管理的?

要想回答这个问题,就得先搬出前面反复提到的Client接口了。

在SpringCloud的环境下,有一个Client的实现,叫LoadBalancerFeignClient

通过名字就可以看出,带有负载均衡的Client实现,负载均衡的实现肯定是交给Ribbon来实现的

所以当Feign配合Ribbon时用的就是这个Client实现

既然实现了Client接口,那就看看execute方法的实现逻辑

图中getClientConfig方法就是判断使用Feign或者Ribbon配置的核心逻辑

核心的判断逻辑就是这一行

options == DEFAULT_OPTIONS

DEFAULT_OPTIONS就是一个超时时间的常量

当上述判断条件成立时,就会通过this.clientFactory.getClientConfig(clientName)获取到Ribbon配置

由于这是Ribbon的逻辑,这里就不深扒了,知道是这个意思就行

当条件不成立时,用Options构建一个FeignOptionsClientConfig

FeignOptionsClientConfig就是简单地将Options配置读出来,设置到父类DefaultClientConfigImpl超时时间配置上

DefaultClientConfigImpl就算你不知道是什么也无所谓,你能看出的一件事就是,超时时间用的是传递给ClientOptions参数

所以,综上,我们的问题就变得非常easy了,那就是什么时候

options == DEFAULT_OPTIONS

只有当这个条件成立时,才使用Ribbon的配置。

这里我们先来捋一捋前面提到的东西

前面我们反复提到,ClientOptions最终只来自于两种配置

  • Feign.Builder

  • 方法参数

所以DEFAULT_OPTIONS这个Options一定是通过上面两种方法中的其中一种设置的

而方法参数是不可能设置的成DEFAULT_OPTIONS

因为这是我们控制的,只要我们参数不传DEFAULT_OPTIONS,那么永远都不可能是DEFAULT_OPTIONS

此时只剩下一种情况,那就是Spring在构建在Feign.Builder的时候,设置成DEFAULT_OPTIONS

通过查找DEFAULT_OPTIONS的使用,我们可以追踪到这么一段代码

这不就是前面提到的通过声明Bean的方式来设置超时时间

不同的是它加了@ConditionalOnMissingBean,这个注解就是说,一旦我们自己没有声明Options,就用他这个Options

到这终于真像大白了。

我们不设置超时时间,Spring就会给Feign.Builder加一个DEFAULT_OPTIONS这个Options

在执行的时候,发现是DEFAULT_OPTIONS,说明我们没有主动设置过超是时间,就会使用Ribbon的超时时间。

为了方便理清上面的逻辑,这里整一张图

虽然Feign可以使用Ribbon的超时时间,但是Ribbon的配置的优先级是最最低的

方法参数 > Feign配置文件 > 声明Options > Ribbon配置

Feign or Ribbon配置用哪个好?

其实我个人更倾向于使用Ribbon的配置方式。

因为Ribbon除了可以设置超时时间之外,还可以配置重试机制、负载均衡等其它的配置

为了简化和统一管理配置,使用Ribbon来配置超时时间。

可能你会有疑问,Feign也支持重试机制,为什么不选择Feign?

这是因为Feign重试机制没有Ribbon的好

Ribbon重试的时候会换一个服务实例来重试,因为原来出错的可能不可用

而Feign并不会换一个服务实例重试,他并不知道上一次使用的是哪个服务实例,这就导致可能会出现在一个不可用的服务实例上多次重试的情况。

引入Hystrix时超时时间设置

如果你之前的确没有研究过关于Feign超时时间的配置关系,那么此时你应该有所收获了。

但是这就结束了么?

不,事情没那么简单。

如果你的项目中使用了Hystrix,那么就得小心前面说的那些配置了。

由于Hystrix跟Feign毕竟是一家人,所以当引入Hystrix时,Feign就跟之前不一样了。

Hystrix会去干一件事,那就是给每个Feign的http接口保护起来,毕竟Hystrix就是干保镖这个事的。

但是这没保护还好,一保护问题就不自觉地出现了。

Hystrix在保护的时候,一旦发现被保护的接口执行的时间超过Hystrix设置的最大时间,就直接进行降级操作。

怎么降级的,这里咱不关心,咱关心的是这个Hystrix超时的最大值是多少。

因为一旦这个时间小于Feign的超时时间,那么就会出现Http接口正在执行,也没有异常,仅仅是因为执行时间长,就被降级了。

而Hystrix的默认的超时时间的最大值就只有1s。

所以就算你Feign超时时间设置的再大,超过1s就算超时,然后被降级,太坑了。。

所以我们需要修改这个默认的超时时间的最大值,具体的配置项如下

hystrix:command:default:execution:isolation:thread:timeoutInMilliseconds: 30000

并且时间上大致要符合下面这个原则

Hystrix超时时间 >= (连接超时时间 + 读超时时间) * 重试次数

重试次数我们前面也提到了,虽然一般我们不设置,但是为了严谨还是得加上,因为一次Http接口的执行时间肯定跟重试次数有关,重试次数越多,时间就越长。

而连接超时时间 + 读超时时间设置方式,前面提到很多次,不论是通过Feign本身设置还是通过Ribbon来设置,都是可以的

总结

今天给大家扒了扒在不同使用条件下Feign的超时时间设置,总结起来大致如下:

  • 单独使用Feign时:通过Feign.Builder和方法参数

  • SpringCloud环境下单独使用Feign:方法参数、配置文件、声明Options Bean

  • 跟Ribbon配合使用:通过Ribbon的超时参数设置

  • 跟Hystrix配合使用:修改默认的超时时间,尽量符合 Hystrix超时时间 >= (连接超时时间 + 读超时时间) * 重试次数

如果本篇文章对你所有帮助,欢迎转发、点赞、收藏、在看,非常感谢。

联系方式

关于文章中大家有任何疑问可以通过关注公众号《编程乐学》进行留言,同时,公众号还有更多有趣的项目以及关于学习编程的笔记资料大家可以看看,欢迎大家进行留言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/579426.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【easy-ES使用】1.基础操作:增删改查、批量操作、分词查询、聚合处理。

easy-es、elasticsearch、分词器 与springboot 结合的代码我这里就不放了&#xff0c;我这里直接是使用代码。 基础准备&#xff1a; 创建实体类&#xff1a; Data // 索引名 IndexName("test_jc") public class TestJcES {// id注解IndexId(type IdType.CUSTOMI…

Python内置数据类型等入门语(句)法

内置数据类型 数字&#xff08;Number&#xff09;关键字: int 、float、complex字符串&#xff08;String&#xff09;关键字&#xff1a;单引号&#xff0c;双引号 三引号都可以表示&#xff0c;8 种内置类型都可转为字符串类型列表&#xff08;List&#xff09; 关键符号 […

SpringBoot整合JWT+Spring Security+Redis实现登录拦截(一)登录认证

一、JWT简介 JWT 全称 JSON Web Token&#xff0c;JWT 主要用于用户登录鉴权&#xff0c;当用户登录之后&#xff0c;返回给前端一个Token&#xff0c;之后用户利用Token进行信息交互。 除了JWT认证之外&#xff0c;比较传统的还有Session认证&#xff0c;如何选择可以查看之前…

Spring中常见的BeanFactory后处理器

常见的BeanFacatory后处理器 先给出没有添加任何BeanFactory后处理器的测试代码 public class TestBeanFactoryPostProcessor {public static void main(String[] args) {GenericApplicationContext context new GenericApplicationContext();context.registerBean("co…

JAVA复习三——CH5 Java Collection 、CH6 MultiThread

CH5 Java Collection(集合) 5.1 Java集合框架&#xff08;位于java.util包中&#xff09; 图一 集合框架图 从上面的集合框架图可以看到&#xff0c;Java 集合框架主要包括两种类型的容器&#xff0c;一种是集合&#xff08;Collection&#xff09;&#xff0c;存储一个元素集…

信息犯罪与计算机取证

1.信息安全 信息安全的三种定义p2 ISO的 为数据处理系统建立和采取的技术和管理的安全保护&#xff0c;保护计算机硬件&#xff0c;软件数据不因偶尔或恶意的原因而受到破坏&#xff0c;更改和泄露 欧盟的 在既定的密级条件下&#xff0c;网络与信息系统抵御意外或恶意行为的能…

【C++】const 关键字

想要正确理解const关键字&#xff0c;只需记住一句话&#xff1a; cosnt关键字优先修饰左边&#xff0c;如果左边每东西&#xff0c;就作用于右边。 const int a; 修饰int a 不能改变 const int *a ; int const *a; 修饰int 指针a指向的地址可以改变&#xff0c;但是地址中…

flask文件夹列表改进版--Bug追踪

把当前文件夹下的所有文件夹和文件列出来&#xff0c;允许点击返回上层目录&#xff0c;允许点击文件夹进入下级目录并显示此文件夹内容 允许点击文件进行下载 from flask import Flask, render_template, send_file, request, redirect, url_for import osapp Flask(__name_…

抖店只能做和营业执照对照的产品吗?开店基础教程,新手可收藏!

我是王路飞。 抖店的营业执照有多重要呢&#xff1f;关系到你店铺的类型、类目和产品。 尤其是适合新手做的个体店&#xff0c;不涉及对公账户&#xff0c;货款可以直接提现到你的私人银行卡里&#xff0c;保证金也只有企业店铺的一半。 &#xff08;只需要身份证就能开通的…

深入Apache Commons Config:管理和使用配置文件

第1章&#xff1a;引言 咱们都知道&#xff0c;在软件开发中&#xff0c;管理配置文件是一件既重要又让人头疼的事。想象一下&#xff0c;咱们的应用程序有一堆设置需要调整&#xff0c;比如数据库的连接信息、应用的端口号&#xff0c;或者是一些功能的开关。如果这些信息硬编…

uni-app 命令行创建

1. 首先创建项目&#xff0c;命令如下: npx degit dcloudio/uni-preset-vue#vite-ts uni-app-demo如果出现报错&#xff0c;如下图. 大概率就是没有目录C:\Users\Administrator\AppData\Roaming\npm 解决办法&#xff1a; 创建目录 C:\Users\Administrator\AppData\Roaming\n…

基于nodemailer实现邮件发送、附件发送、多人发送

文章目录 1、QQ邮箱如何设置授权码2、具体代码 1、QQ邮箱如何设置授权码 QQ邮箱SMTP/IMAP服务 1、点击账号与安全 2、安全设置 3、设备管理&#xff0c;可以查看有多少个授权码 2、具体代码 from 这个参数&#xff0c;有两种写法 qq号qq.com"姓名"<qq号qq.co…

UDP Ping程序实现--第5关:客户端向服务器发送消息并接收消息

✨创作不易&#xff0c;还希望各位大佬支持一下 &#x1f44d; 点赞&#xff0c;你的认可是我创作的动力&#xff01; ⭐️ 收藏&#xff0c;你的青睐是我努力的方向&#xff01; ✏️ 评论&#xff0c;你的意见是我进步的财富&#xff01; 任务描述 本关任务&#xff1a;P…

【数据库系统概论】第3章-关系数据库标准语言SQL(2)

文章目录 3.4 数据查询3.4.1 单表查询3.4.2 连接查询3.4.3嵌套查询3.4.4 集合查询3.4.5 基于派生表的查询3.4.6 select 语句的目标列 3.4 数据查询 格式 SQL执行顺序 3.4.1 单表查询 基础查询 select * from student // 不重复 select distinct sname from student // 命名…

C++ Lambda表达式的完整介绍

c在c11标准中引入了lambda表达式&#xff0c;一般用于定义匿名函数&#xff0c;使得代码更加灵活简洁。lambda表达式与普通函数类似&#xff0c;也有参数列表、返回值类型和函数体&#xff0c;只是它的定义方式更简洁&#xff0c;并且可以在函数内部定义。 什么是Lambda表达式…

【番外】在Windows安装Airsim/UE4踩坑合集

在Windows安装Airsim/UE4踩坑合集 1.安装过程中一定要确保Epic Games Launcher是英文环境&#xff0c;保存路径什么的也尽量是英文。2.UE4中的虚幻引擎一定要安装4.27版本以上的&#xff0c;不然的话最后运行vs的时候会报语法错误&#xff0c;网上根本查不到的那种错误。换了版…

代码随想录 213. 打家劫舍 II

题目 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋&#xff0c;每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 &#xff0c;这意味着第一个房屋和最后一个房屋是紧挨着的。同时&#xff0c;相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在…

Servlet技术之Listener监听器

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 Servlet技术之Listener监听器 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、什么是…

谷歌被曝或再次大裁员!3万员工面临被AI取代

据报道&#xff0c;继1.2万大裁员之后&#xff0c;谷歌又计划重组广告销售部门——这将导致3万名员工面临裁员的风险。 这一年的科技行业&#xff0c;可以说是从年头裁到了年尾&#xff0c;还越裁越多了。 而这次谷歌的部门重组计划&#xff0c;让打工人们发现&#xff0c;除…

运维工程师的出路:探索IT界“万金油”的职业发展之旅

运维工程师的未来之路:不只是IT界的“万金油” 当下,运维工作似乎正处在一个多事之秋,一方面他们必须应对技术的迅速更迭和业务需求的不断提升,另一方面,关于"35岁危机"的论调也不绝于耳。运维人员究竟该如何面对自己的职业生涯,以及他们的出路到底在哪里?这…