最近收到一个线上项目反馈,客户在调用我司的 openAPI 接口时频繁报请求超时的错误。去线上查看相关日志时确实发现很多 ERROR 日志:
1
2
3
4 BusinessClient#create(JSONObject) failed and no fallback available.
com.netflix.hystrix.exception.HystrixRuntimeException: BusinessClient#create(JSONObject) failed and no fallback available.
...这些异常出现的原因是因为
BusinessClient#create
接口的响应太久导致 Ribbon 超时,从而触发 Hystrix 的熔断,所以第一时间便是去修改了相关的超时配置:
1
2
3
4 feign.hystrix.enabled = true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds = 600000
ribbon.ConnectTimeout = 1000
ribbon.ReadTimeout = 300000本以为可以轻松解决,但事与愿违,修改配置后依然出现了超时异常,并且追踪日志发现超时的时间是 1 分钟,并不是配置中的时间。于是乎便有了以下对 Feign、Ribbon 和 Hystrix 超时时间的分析。
首先需要明确的是在 Spring Cloud 中,通常是将 Feign、Ribbon 和 Hystrix 组合使用。Feign 负责服务调用,Ribbon 提供负载均衡,Hystrix 负责熔断和容错
Feign 是一种声明式的 HTTP 客户端,它简化了服务间的调用过程。通过 Feign,开发者可以使用接口来定义服务,而不必直接编写 HTTP 请求的细节
Ribbon 是一个客户端负载均衡器,用于在微服务架构中实现服务的负载均衡。当多个服务实例存在时,Ribbon 会根据配置的负载均衡策略(如轮询、随机、权重等)自动选择一个可用的服务实例去执行请求
Hystrix 是 Netflix 开源的一个容错和延迟处理的库,主要用于防止单个服务故障影响整个系统。Hystrix 提供了熔断、降级、隔离等机制,能有效地提高服务的稳定性和容错性
Feign 的超时时间
先说明一个关键点,在使用 @FeignClient
声明一个 Feign 客户端时,如果未指明 url 属性值时,Feign 会通过注册中心查找指定服务进行请求,如果指明了 url,那么 Feign 仅作为一个 Http 客户端向指明的 url 发起请求
简而言之,言而总之,如果在 @FeignClient 中配置了 url 属性,Feign 就不会使用 Ribbon 来进行负载均衡,也就是 Ribbon 的超时时间不会生效
通过 @FeignClient
进入到 Feign 的源码,可以找到一个用来解析和配置 Feign 客户端属性的关键类
org.springframework.cloud.netflix.feign.FeignClientFactoryBean
1 | // 定义了一个 FeignClientFactoryBean 类,用于通过 Spring 的 FactoryBean 机制创建 Feign 客户端代理 |
当指定 url 时,创建默认的 Client 用来发起 Http 请求
1 | public interface Client { |
在 Feign 中,Options
类用于配置 HTTP 请求的超时时间,包括连接超时和读取超时。通过自定义 Options
实例,可以灵活地为 Feign 客户端配置这些超时时间
全局配置 Options
1 |
|
针对某个 Feign 客户端的配置
1 |
|
通过 Feign.Builder 自定义 Options
1 | Feign.Builder builder = Feign.builder().options(new Request.Options(3000, 8000)); // 3秒连接超时,8秒读取超时 |
这种方式适用于不依赖 Spring 的纯 Feign 中,可以直接通过 Feign.Builder
来设置自定义 Options
Ribbon 的超时时间
Ribbon 通过一个实现 Client 接口的类来完成请求的负载均衡
org.springframework.cloud.netflix.feign.ribbon.LoadBalancerFeignClient
1 | // 定义了一个负载均衡的 Feign 客户端类,实现了 Feign 的 Client 接口 |
Hystrix 超时时间
com.netflix.hystrix.HystrixCommandProperties
中设置了 Hystrix 相关配置参数
1 | protected HystrixCommandProperties(HystrixCommandKey key, HystrixCommandProperties.Setter builder, String propertyPrefix) { |
需要注意的是,要想 Hystrix 在配置的时间下熔断,需要在 @FeignClient 中配置 fallback
属性
Feign 的客户端
顺带看到 Feign 在整合 Ribbon 的过程中,有提供两种默认的请求客户端 Apache HttpClient
和 OkHttp
org.springframework.cloud.netflix.feign.ribbon.HttpClientFeignLoadBalancedConfiguration
1 |
|
org.springframework.cloud.netflix.feign.ribbon.OkHttpFeignLoadBalancedConfiguration
1 |
|
无独有偶,之前使用 Feign 作为 Http 客户端的时候遇到无法处理 PATCH 请求的情况,所以可以对 Feign 自定义客户端发送请求
1 |
|
总结
Feign 自身就是一个 Http 客户端,它有自己的超时时间,以及重试等机制。在 Feign 的基础之上,整合 Ribbon 和 Hystrix 组件形成一套完整的分布式系统 RPC 组件。在使用 @FeignClient 注解声明 Feign 客户端时,主要就是通过 url 是否有值来判断是走单纯的 http 请求还是走注册中心的服务发现来结合 Ribbon 做负载均衡。这里是十分容易「踩坑」的地方。