在微服务架构中很多功能都需要调用多个服务才能完成某一项功能,一个成熟的微服务集群,内部调用必然依赖一个好的 RPC 框架,比如:基于 Http 协议的 Feign
,基于私有 tcp 协议的 Dubbo
1. Feign 是什么
Feign 是Spring Cloud Netflix组件中的轻量级Restful的 HTTP 服务客户端,实现了负载均衡和 Rest 调用的开源框架,封装了Ribbon和RestTemplate, 实现了WebService的面向接口编程,进一步降低了项目的耦合度。
Feign 通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,封装了 Http 调用流程。
1、Robbin
(1)客户端的软负载
Robbin是springcloud的LB调用组件,提供客户端的软件负载均衡。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随即连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。
Robbin 提供的客户端软负载,是SpringCloud微服务的典型特征之一。
2)负载均衡策略
- 轮询(RoundRobin),以轮询的方式依次将请求调度不同的服务器,即每次调度执行i = (i + 1) mod n,并选出第i台服务器。
- 随机(Random),随机选择状态为UP的Server。
- 加权响应时间(WeightedResponseTime),根据相应时间分配一个weight,相应时间越长,weight越小,被选中的可能性越低。
- 区域感知轮询(ZoneAvoidanceRule),复合判断server所在区域的性能和server的可用性选择server。
(3)核心组件
Ribbon的核心组件(均为接口类型)有以下几个,
- ServerList,用于获取地址列表。它既可以是静态的(提供一组固定的地址),也可以是动态的(从注册中心中定期查询地址列表)。
- ServerListFilter,仅当使用动态ServerList时使用,用于在原始的服务列表中使用一定策略过虑掉一部分地址。
- IRule,选择一个最终的服务地址作为LB结果。选择策略有轮询、根据响应时间加权、断路器(当Hystrix可用时)等。
Ribbon在工作时首选会通过ServerList来获取所有可用的服务列表,然后通过ServerListFilter过虑掉一部分地址,最后在剩下的地址中通过IRule选择出一台服务器作为最终结果。
2、Fegin
Fegin是一个声明式Http端调用,集成了Robbin的负载均衡功能,同时声明式调用更加方便(只需要简单的注解即可)。简单的可以理解为:Spring Cloud Feign 的出现使得Eureka和Ribbon的使用更加简单。
(1)Fegin接口示例
a、启动类@EnableFeignClients 注解
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients // 启用fegin声明式调用
public class FeginComsumerApplication {
public static void main(String[] args) {
SpringApplication.run(FeginComsumerApplication.class, args);
}
}
b、声明一个调用的Feign接口,
@Service
@FeignClient(name = "name-service")
public interface NameService {
@RequestMapping(value = "/getName", method = RequestMethod.GET)
public String getName();
}
c、服务端提供接口实现
@RequestMapping(value = "/getName", method = RequestMethod.GET)
public String getName(){
return "hello world";
}
d、fegin声明是调用
@Autowired
private NameServiceClient feginNameServiceClient;
@RequestMapping(value = "/getName", method= RequestMethod.GET)
public String getName(){
return feginNameServiceClient.getName();
}
(2)Fegin 的类加载流程
- 通过主类上的EnableFeignClients 注解开启FeignClient;
- 根据Feign 的规则实现接口,并加上FeignClient注解,供调用的地方注入调用;
- 程序启动后,会扫描所有FeignClient 注解的类,并将这些信息注入到IOC 容器中;
- 当b中接口被调用时,通过jdk代理,以及反射(Spring处理注解的方式),来生成具体的RequestTemplate
- RequestTemplate 生成Reqest
- Request 交给httpclient处理,这里的httpclient 可以是OkHttp,也可以是HttpUrlConnection 或者HttpClient
- 最后Client被封装到LoadBalanceClient类,这个类结合Ribbon 实现负载均衡。
(3)Fegin的原理
3、Hystrix
Hystrix 是springcloud生态的断路器(隔离、限流、降级),主要是用来预防服务雪崩的现象,剔除掉分布式系统中某些挂掉或请求过慢的服务节点。Hystrix是一个帮助解决分布式系统中超时处理和容错的类库, 拥有保护系统的能力。
(1)隔离、限流、降级
Hystrix断路器有两种隔离策略:信号量隔离(默认)和线程池隔离。
信号量模式从始至终都只有请求线程自身,是同步调用模式,不支持超时调用,不支持直接熔断,由于没有线程的切换,开销非常小。
线程池模式可以支持异步调用,支持超时调用,支持直接熔断,存在线程切换,开销大。
信号量隔离:常用于获取共享资源的场景中,比如计算机连接了两个打印机,那么初始的信号量就是2,被某个进程或线程获取后减1,信号量为0后,需要获取的线程或进程进入资源等待状态。Hystrix的处理有些不同,其不等待,直接返回失败。
线程池隔离:采用的就是jdk的线程池,其默认选用不使用阻塞队列的线程池,例如线程池大小为10,如果某时刻10个线程均被使用,那么新的请求将不会进入等待队列,而是直接返回失败,起到限流的作用。
此外,其还引入了一个断路器机制,当断路器处于打开状态时,直接返回失败或进入降级流程。断路器打开和关闭的触发流程为:当总的请求数达到可阈值HystrixCommandProperties.circuitBreakerRequestVolumeThreshold(),或总的请求失败百分比达到了阈值HystrixCommandProperties.circuitBreakerErrorThresholdPercentage(),这时将断路器的状态由关闭设置为打开。当断路器打开时,所有的请求均被短路,在经过指定休眠时间窗口后,让下一个请求通过(断路器被认为是半开状态)。如果请求失败,断路器进入打开状态,并进入新的休眠窗口;否则进入关闭状态。
流程如上图所示,Hystrix框架通过命令模式来实现方法粒度上的服务保障,主要涉及HystrixCommand和HystrixObservableCommand类,前者提供同步的execute和异步的queue方法,后者提供立即执行observe和延迟执行toObservable的回调方法。此外,实际项目中通常不会使用Hystrix集成的本地缓存。
配置问题
Feign 如何设置超时时间(connectionTimeout、readTimout
对于这个问题网上有很多相关资料,大体上有两种方案,一种是通过设置 ribbon 的超时时间(因为 Feign 是基于 ribbon 来实现的,所以通过 ribbon 的超时时间设置也能达到目的),一种是直接设置 Feign 的超时时间,我将会在下边的篇幅里分别说一下如何通过application.yml 配置文件来设置超时时间
1、Ribbon
对于 ribbon 又分为全局配置和指定服务配置:
- 全局配置
对所有的服务该配置都生效ribbon: ReadTimeout: 30000 #单位毫秒 ConnectTimeout: 30000 #单位毫秒
- 指定服务配置
下边代码中的 jettopro-basic 是服务的名称,意思是该配置只针对名为 annoroad-beta 的服务有效,根据实际的需要替换成你自己的服务名jettopro-basic: ribbon: ReadTimeout: 30000 #单位毫秒 ConnectTimeout: 30000 #单位毫秒
2、Feign
与 Ribbon 一样,Feign 也分为全局配置和指定服务配置:
- 全局配置
下边代码中使用的 feign.client.config.default ,意思是所有服务都采用该配置
feign:
client:
config:
default:
connectTimeout: 10000 #单位毫秒
readTimeout: 10000 #单位毫秒
- 指定服务配置
下边代码中使用的 feign.client.config.jettopro-basic,意思是该配置只针对名为 jettopro-basic 的服务有效,可以根据实际的需要替换成你自己的服务名
feign:
client:
config:
jettopro-basic:
connectTimeout: 10000 #单位毫秒
readTimeout: 10000 #单位毫秒
3、总结
如果同时配置了Ribbon、Feign,那么 Feign 的配置将生效
Ribbon 的配置要想生效必须满足微服务相互调用的时候通过注册中心,如果你是在本地通过 @FeignClient 注解的 url 参数进行服务相互调用的测试,此时 ribbon 设置的超时时间将会失效,但是通过 Feign 设置的超时时间不会受到影响(仍然会生效)
综上所述建议使用 Feign 的来设置超时时间
基本介绍
1. Feign 是什么
Feign 是Spring Cloud Netflix组件中的轻量级Restful的 HTTP 服务客户端,实现了负载均衡和 Rest 调用的开源框架,封装了Ribbon和RestTemplate, 实现了WebService的面向接口编程,进一步降低了项目的耦合度。
Feign 通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,封装了 Http 调用流程。
2. 为什么要使用 Feign
如果不使用 RPC 框架,那么调用服务需要走 Http 的话,配置请求 head、body,然后才能发起请求。获得响应体后,还需解析等操作,十分繁琐。
Feign 旨在使编写 JAVA HTTP 客户端变得更加简单,Feign 简化了RestTemplate代码,实现了Ribbon负载均衡,使代码变得更加简洁,也少了客户端调用的代码,使用 Feign 实现负载均衡是首选方案,只需要你创建一个接口,然后在上面添加注解即可。
Feign 是声明式服务调用组件,其核心就是:像调用本地方法一样调用远程方法,无感知远程 HTTP 请求。让开发者调用远程接口就跟调用本地方法一样的体验,开发者完全无感知这是远程方法,无需关注与远程的交互细节,更无需关注分布式环境开发。
3. OpenFeign
Feign 内置了Ribbon,用来做客户端负载均衡调用服务注册中心的服务。
Feign 支持的注解和用法参考官方文档:https://github.com/OpenFeign/feign官方文档,使用 Feign 的注解定义接口,然后调用这个接口,就可以调用服务注册中心的服务。
Feign本身并不支持Spring MVC的注解,它有一套自己的注解,为了更方便的使用Spring Cloud孵化了OpenFeign。并且支持了Spring MVC的注解,如@RequestMapping,@PathVariable等等。
OpenFeign的@FeignClient可以解析Spring MVC的@RequestMapping注解下的接口,并通过动态代理方式产生实现类,实现类中做负载均衡调用服务。
- Feign 采用的是基于接口的注解
- Feign 整合了 Ribbon,具有负载均衡的能力
- 整合了 Hystrix,具有熔断的能力
一、Feign 和 Ribbon
1. 设置 OpenFeign 的超时时间
我们首先来看一下 OpenFeign
自己的请求超时配置,直接在 yml 文件中配置:
eign:
# 设置 feign 超时时间
client:
config:
# default 设置的全局超时时间,指定服务名称可以设置单个服务的超时时间
default:
connectTimeout: 5000
readTimeout: 5000
default 默认是全局的,将 default 换成某个服务的名称可以设置单个服务的超时时间
2. 设置 Ribbon 的超时时间
ribbon:
# 建立链接所用的时间,适用于网络状况正常的情况下, 两端链接所用的时间
ReadTimeout: 5000
# 指的是建立链接后从服务器读取可用资源所用的时间
ConectTimeout: 5000
注意这两个参数设置的时候没有智能提示
ConnectTimeout:
指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间。
在java中,网络状况正常的情况下,例如使用 HttpClient 或者 HttpURLConnetion 连接时设置参数 connectTimeout=5000 即5秒,如果连接用时超过5秒就是抛出 java.net.SocketException: connetct time out 的异常。
ReadTimeout:
指的是建立连接后从服务器读取到可用资源所用的时间。
在这里我们可以这样理解ReadTimeout:正常情况下,当我们发出请求时可以收到请求的结果,也就是页面上展示的内容,但是当网络状况很差的时候,就会出现页面上无法展示出内容的情况。另外当我们使用爬虫或者其他全自动的程序时,无法判断当前的网络状况是否良好,此时就有了ReadTimeout的用武之地了,通过设置ReadTimeout参数,例:ReadTimeout=5000,超过5秒没有读取到内容时,就认为此次读取不到内容并抛出Java.net.SocketException: read time out的异常。
源码分析
可以发现
OpenFeign
的默认的connectTimeout
是 10 秒,readTimeout
是 60 秒
此时,我们是要验证 OpenFeign
的默认超时时间,所以在 application.yml
中 feign 和 ribbon 的超时时间都没有设置。报错了,连接超时,可是我们代码里睡 5 秒,明明还在超时时间范围内,怎么就连接超时了呐?其实 OpenFeign
集成了 Ribbon
,Ribbon 的默认超时连接时间、读超时时间都是 1 秒
如果 OpenFeign
没有设置对应得超时时间,那么将会采用 Ribbon
的默认超时时间
- 设置
OpenFeign
超时时间feign: client: config: default: connectTimeout: 8000 readTimeout: 8000
- 设置
Ribbon
超时时间 -
ribbon: ReadTimeout: 7000 ConectTimeout: 7000
重复上面步骤,断点进去一看 ??? 怎么还是 8000
原因是
OpenFeign
和Ribbon
的超时时间只会有一个生效两者是二选一的,且OpenFeign
优先
注掉 OpenFeign 超时时间配置之后,就变成了使用设置的 Ribbon 的超时时间
4. 结论
Feign 和 Ribbon 的超时时间只会有一个生效,规则:如果没有设置过 feign 超时,也就是等于默认值的时候,就会读取 ribbon 的配置,使用 ribbon 的超时时间和重试设置。否则使用 feign 自身的设置。两者是二选一的,且 feign 优先。
二、Ribbon 和 Hystrix
1. Hystrix 设置超时时间
# 设置 hystrix 超时时间
feign:
hystrix:
enabled: true
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 6000
@FeignClient(contextId = "remoteUserService", value = "cloud-system", fallbackFactory = RemoteUserFallbackFactory.class)
注意:如果没有配置 fallback,那么 hystrix 的超时就不会生效,而是由 ribbon 来控制。
hystrix 的默认超时时间是 1s,这个配置在 HystrixCommandProperties 类
设置 hystrix 超时时间比 ribbon 大(OpenFign 的超时时间注掉)
ribbon:
ReadTimeout: 2000
ConectTimeout: 2000
访问地址 http://localhost:9203/test/getUserInfo?userId=2
发现请求 2s 左右就返回了,这个值刚好是 ribbon.ReadTimeout 的时间。表示此时 ribbon 超时触发了。然后进入了 hystrix 的熔断过程。
2. 结论:
- 如果请求时间超过 ribbon 的超时配置,会触发重试;
- 在配置 fallback 的情况下,如果请求的时间(包括 ribbon 的重试时间),超出了 ribbon 的超时限制,或者 hystrix 的超时限制,那么就会熔断。
一般来说,会设置 ribbon 的超时时间 < hystrix, 这是因为 ribbon 有重试机制。(这里说的 ribbon 超时时间是包括重试在内的,即,最好要让 ribbon 的重试全部执行,直到 ribbon 超时被触发)。
由于 connectionTime 一般比较短,可以忽略。那么,设置的超时时间应该满足:
(1 + MaxAutoRetries) * (1 + MaxAutoRetriesNextServer)* ReadTimeOut < hystrix 的 *timeoutInMillisecond
微服务系列:服务调用 Spring Cloud 之 OpenFeign 详细入门_程序猿秃头之路的博客-CSDN博客
微服务系列:服务调用 Spring Cloud 之 OpenFeign 性能优化_程序猿秃头之路的博客-CSDN博客
微服务系列:Spring Cloud 之 Feign、Ribbon、Hystrix 三者超时时间配置_feign和ribbon超时时间设置_程序猿秃头之路的博客-CSDN博客
错误1:springcloud异常:timed-out and no fallback available,failed and no fallback available的问题解决
timed-out and no fallback available:
这个错误基本是出现在Hystrix熔断器,熔断器的作用是判断该服务能不能通,如果通了就不管了,调用在指定时间内超时时,就会通过熔断器进行错误返回。
一般设置如下配置的其中一个即可:
1、把时间设长
这里设置5秒
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
2、把超时发生异常属性关闭
hystrix.command.default.execution.timeout.enabled=false
3、禁用feign的hystrix
feign.hystrix.enabled: false
failed and no fallback available:
而通过上面设置只是针对熔断器的错误关闭,并不能解决根本问题,比如Feign客户端调用远程服务时,默认为8秒超时时间,如果在规定时间内没有返回,同样会跳转到熔断器进行处理。即使关闭了熔断器的错误,但是总的错误处理还会是有这个问题出现。
那么要解决根本问题,就要从请求超时时间入手,因为有些服务可能存在调用时间长的问题,所以直接配置:
ribbon.ReadTimeout=60000
ribbon.ConnectTimeout=60000
这些才是真正解决请求超时的问题,如果不设置这个,被调用接口很慢时,会出现Read Timeout on Request。
而针对调用失败重试的次数也可以设置:
ribbon.maxAutoRetries=0
参考:
-
https://github.com/spring-cloud/spring-cloud-netflix/issues/321
-
https://stackoverflow.com/questions/38080283/how-to-solve-timeout-feignclient
-
http://www.itmuch.com/spring-cloud-feign-ribbon-first-request-fail/
-
https://github.com/spring-cloud/spring-cloud-netflix/issues/696
-
http://www.jianshu.com/p/0eb13fd033a8
-
http://blog.csdn.net/qwlzxx/article/details/77163268
-
http://blog.csdn.net/clementad/article/details/54315805