本文由 简悦 SimpRead 转码, 原文地址 www.toutiao.com
如果不做特殊配置,OpenFeign 默认使用 jdk 自带的 HttpURLConnection,我们知道 HttpURLConnection 没有连接池
作者 | jinjunzhu
来源 | jinjunzhu
OpenFeign 是 SpringCloud 中的重要组件,它是一种声明式的 HTTP 客户端。使用 OpenFeign 调用远程服务就像调用本地方法一样,但是如果使用不当,很容易踩到坑。
feign 中 http client
如果不做特殊配置,OpenFeign 默认使用 jdk 自带的 HttpURLConnection,我们知道 HttpURLConnection 没有连接池、性能和效率比较低,如果采用默认,很可能会遇到性能问题导致系统故障。
可以采用 Apache HttpClient,properties 文件中增加下面配置:
|
|
|
|
也可以采用 OkHttpClient,properties 文件中增加下面配置:
|
|
pom 文件中增加依赖:
|
|
通过 OpenFeign 作为注册中心的客户端时,默认使用 Ribbon 做负载均衡,Ribbon 默认也是用 jdk 自带的 HttpURLConnection,需要给 Ribbon 也设置一个 Http client,比如使用 okhttp,在 properties 文件中增加下面配置:
|
|
坑二:全局超时时间
OpenFeign 可以设置超时时间,简单粗暴,设置一个全局的超时时间,如下:
|
|
如果不配置超时时间,默认是连接超时 10s,读超时 60s,在源码 feign.Request 的内部类 Options 中定义。
这个接口设置了最大的 readTimeout 是 60s,这个时间必须大于调用的所有外部接口的 readTimeout,否则处理时间大于 readTimeout 的接口就会调用失败。
如下图,在一个系统中使用 OpenFeign 调用外部三个服务,每个服务提供两个接口,其中 serviceC 的一个接口需要 60 才能返回,那上面的 readTimeout 必须设置成 60s。
但是如果 serviceA 出故障了,表现是接口 1 超过 60s 才能返回,这样 OpenFeign 只能等到读超时,如果调用这个接口的并发量很高,会大量占用连接资源直到资源耗尽系统奔溃。要防止这样的故障发生,就必须保证接口 1 能 fail-fast。最好的做法就是给 serviceC 单独设置超时时间。
坑三:单服务设置超时时间
从上一节的讲解我们看到,需要对 serviceC 单独设置一个超时时间,代码如下:
|
|
这个时间会覆盖第一节中默认的超时时间。但是问题又来了,serviceC 中又掉了 serviceD,因为 serviceD 的故障导致接口 6 发生了读超时的情况,为了不让系统奔溃,不得不对 serviceC 的接口 5 单独设置超时时间。如下图:
坑四:熔断超时时间
怎样给单个接口设置超时时间,查看网上资料,必须开启熔断,配置如下:
|
|
开启熔断后,就可以给单个接口配置超时了。如果调用 serviceC 的接口 5 的声明如下:
|
|
根据上面 interface5 接口的声明,在 properties 文件中增加如下配置:
|
|
网上资料说的并不准确,这个超时时间并没有起作用。为什么不生效呢?
使用 feign 超时
最终使用的超时时间来自于 Options 类。如果我们配置了 feign 的超时时间,会选择使用 feign 超时时间,下面代码在 FeignClientFactoryBean 类的 configureUsingProperties 方法:
|
|
使用 ribbon 超时
如果没有配置 feign,但是配置了 ribbon 的超时时间,会使用 ribbon 的超时时间。我们看下这段源代码,FeignLoadBalancer 里面的 execute 方法,
|
|
使用自定义 Options
对于单个接口怎么配置超时时间,我这里给出一个方案,如果你有其他方案,欢迎探讨。我的方案是使用 RestTemplate 来调这个接口,单独配置超时时间,配置代码如下,这里使用 OkHttpClient:
|
|
为了使用 ribbon 负载均衡,上面加了 @LoadBalanced
如果使用 RestTemplate,就会使用 OkHttp3ClientHttpRequestFactory 中配置的时间。
坑五:ribbon 超时时间
作为负载均衡,ribbon 超时时间也是可以配置的,可以在 properties 增加下面配置:
|
|
有文章讲 ribbon 配置的超时时间必须要满足接口响应时间,其实不然,配置 feign 的超时时间就足够了,因为它可以覆盖掉 ribbon 的超时时间。
坑六:重试默认不开启
OpenFeign 默认是不支持重试的,可以在源代码 FeignClientsConfiguration 中 feignRetryer 中看出。
|
|
要开启重试,我们可以自定义 Retryer,比如下面这行代码:
|
|
表示每间隔 100ms,最大间隔 1000ms 重试一次,最大重试次数是 1,因为第三个参数包含了第一次请求。
拉取服务列表
Ribbon 默认从服务端拉取列表的时间间隔是 30s,这个对优雅发布很不友好,一般我们会把这个时间改短,如下改成 3s:
|
|
重试
Ribbon 重试有不少需要注意的地方,这里分享 4 个。
- 同一实例最大重试次数,不包括首次调用,配置如下:
|
|
这个次数不包括首次调用,配置了 1,重试策略会先尝试在失败的实例上重试一次,如果失败,请求下一个实例。
- 同一个服务其他实例的最大重试次数,这里不包括第一次调用的实例。默认值为 1:
|
|
- 是否对所有操作都重试,如果改为 true,则对所有操作请求都进行重试, 包括 post,建议采用默认配置 false。
|
|
- 对指定的 http 状态码进行重试
|
|
如下图:
hystrix 默认不开启,但是如果开启了 hystrix,因为 hystrix 是在 Ribbon 外面,所以超时时间需要符合下面规则:hystrix 超时 >= (MaxAutoRetries + 1) * (ribbon ConnectTimeout + ribbon ReadTimeout)
如果 Ribbon 不重试,MaxAutoRetries=0
根据上面公式,假如我们配置熔断超时时间如下:
|
|
这个配置是不会重试一次的。serviceA 调用 serviceB 时,hystrix 会等待 Ribbon 返回的结果,如果 Ribbon 配置了重试,hystrix 会一直等待直到超时。上面的配置,因为第一次请求已经耗去了 8s,剩下时间 7s 不够请求一次了,所以是不会进行重试的。
即使不用注册中心,使用 OpenFeign 做普通 http 客户端也是很方便的,但是有三点需要注意:
- 不用配置 ribbon 相关参数
- 使用 RestTemplate 调用时,不考虑负载均衡
- 使用过程中 OpenFeign 要组装出自己的一套请求,跟直接使用 http 客户端比,会有一定开销
使用 OpenFeign 有很多配置上的坑,对于没有注册中心的情况,建议直接使用 http 客户端