《Spring Cloud Alibaba教程十》- 聊聊Feign消费服务时Ribbon+Hystrix请求超时问题

一、前言

这篇文章的目的是搞清楚在使用Feign客户端调用服务时,遇到的几个超时问题,涉及到Ribbon超时Hystrix超时,博客教程源码使用: 《Spring Cloud Alibaba教程四》- 使用Nacos注册中心之Feign方式消费服务 的源码,在阅读这篇文章前去下载源码导入IDEA并运行,这样理解本篇文章更容易。

下载源码: https://github.com/Thinkingcao/SpringCloudLearning/tree/master/springcloud-ribbon-hystrix

二、什么是Feign超时

1.使用Feign调用接口分为两层,Ribbon的调用和Hystrix的调用,所以Ribbon的超时时间和Hystrix的超时时间的结合就是Feign的超时时间。
2.一般情况下 都是 Ribbon 的超时时间(<)Hystrix的超时时间(因为涉及到Ribbon 的重试机制),如果Ribbon 的超时时间大于Hystrix的超时时间,对于Ribbon 的重试是没有意义的(Hystrix超时熔断了,Ribbon 无法重试)。

三、Ribbon超时

1. 问题: 在服务提供者的接口上加休眠5秒,测试Feign调用的时候是否会超时?
在这里插入图片描述
接口提供方和接口调用方的application.yml都是如下:
在这里插入图片描述

2. 访问接口: http://130.252.102.241:8012/getProduct/1010110 控制台异常如下:

2020-04-30 15:25:13.494 ERROR 17768 --- [nio-8012-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is feign.RetryableException: Read timed out executing GET http://nacos-product/getProductInfo/1010110] with root cause

java.net.SocketTimeoutException: Read timed out
	at java.net.SocketInputStream.socketRead0(Native Method) ~[na:1.8.0_25]
	at java.net.SocketInputStream.read(SocketInputStream.java:150) ~[na:1.8.0_25]
	at java.net.SocketInputStream.read(SocketInputStream.java:121) ~[na:1.8.0_25]
	at java.io.BufferedInputStream.fill(BufferedInputStream.java:246) ~[na:1.8.0_25]
	at java.io.BufferedInputStream.read1(BufferedInputStream.java:286) ~[na:1.8.0_25]
	at java.io.BufferedInputStream.read(BufferedInputStream.java:345) ~[na:1.8.0_25]
	at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:703) ~[na:1.8.0_25]
	at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647) ~[na:1.8.0_25]
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1534) ~[na:1.8.0_25]
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1439) ~[na:1.8.0_25]
	at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480) ~[na:1.8.0_25]
	at feign.Client$Default.convertResponse(Client.java:143) ~[feign-core-10.2.3.jar:na]
	at feign.Client$Default.execute(Client.java:68) ~[feign-core-10.2.3.jar:na]
	at org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.execute(FeignLoadBalancer.java:93) ~[spring-cloud-openfeign-core-2.1.2.RELEASE.jar:2.1.2.RELEASE]

分析: 出现这个异常的时候,起初我不知道究竟是Hystrix断路器走熔断了还是Ribbon处理请求超时了,通过观察日志和做测试,我发现了错误日志是由: org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer包打印出来的,那么我就很确定这是Ribbon处理请求超时了,因为我设置了接口提供方休眠5秒,而Ribbon的默认超时是1秒,而默认情况下在使用Feign时,Hystrix默认是未开启的,这样我就更加肯定这是Ribbon超时,那么如何处理呢,见后文。

org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.execute(FeignLoadBalancer.java:93) ~[spring-cloud-openfeign-core-2.1.2.RELEASE.jar:2.1.2.RELEASE]

解决: 服务调用方application.yml加入Ribbon请求处理超时和连接超时,这里需要注意一下,我们在服务提供方的接口里设置休眠的时间是5秒,所以这里在设置Ribbon的超时时间时,需要大于5秒,不然起不到作用。

ribbon:
  ReadTimeout: 6000
  ConnectTimeout: 5000

再次请求: http://130.252.102.241:8012/getProduct/1010110 , 浏览器响应结果如下:

Hello Nacos Discovery 1010110

所以这就正面我们初步的判断,上述异常在Feign消费服务,Feign中的Hystrix默认是未开启的,所以这个问题是由Ribbon超时引起的。

四、Hystrix超时

1. 问题: 在服务提供者的接口上加休眠5秒,测试Feign调用的时候是否会超时?
在这里插入图片描述
接口提供方的application.yml配置改成如下,为了探究Hystrix我们去掉Ribbon的超时配置,开启Feign中的Hystrix
在这里插入图片描述
2. 访问接口: http://130.252.102.241:8012/getProduct/1010110 控制台异常如下:

2020-04-30 15:53:21.546 ERROR 5156 --- [ HystrixTimer-1] c.t.o.f.c.OrderFeignFallbackFactory      : 调用异常:com.netflix.hystrix.exception.HystrixTimeoutException
2020-04-30 15:53:21.550  INFO 5156 --- [nio-8012-exec-1] c.t.order.controller.OrderController     : 调用服务结束: 开启断路-fallback; reason was: com.netflix.hystrix.exception.HystrixTimeoutException

浏览器返回结果:

开启断路-fallback; reason was: com.netflix.hystrix.exception.HystrixTimeoutException

控制台打印日志:
在这里插入图片描述
分析: 从异常的日志来看,服务调用超时了,超时后Hystrix立即进行了服务的熔断,走了回调方法OrderFeignFallbackFactory,输出了服务熔断后我们定制的Msg,那么如何解决这个超时问题呢?
在这里插入图片描述
解决: 在接口调用方application.yml加入Hystrix超时配置,同样这里需要注意的是,Hystrix默认的超时时间是1秒,而我们在服务提供方的接口里设置休眠的时间是5秒,所以这里在设置Hystrix的超时时间时,需要大于5秒,不然起不到作用防止超时的作用。

hystrix:
  command:
    default:
      execution:
        timeout:
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 60000

此时完整配置如下:
在这里插入图片描述

再次请求: http://130.252.102.241:8012/getProduct/1010110 , 浏览器响应结果如下:

开启断路-fallback; reason was: feign.RetryableException: Read timed out executing GET http://nacos-product/getProductInfo/1010110

控制台异常如下:
在这里插入图片描述
分析: 开启了Hystrix断路器并且设置了Hystrix的超时时间为6秒,但是这个时候Ribbon处理请求却超时了。分析下面这行异常日志:RetryableException 大概意思是重试异常,查阅资料得知,Ribbon在超时后会进行重试的,因为没有对Ribbon超时进行处理,但是开启了Hystirx的断路器功能和设置其超时,所以Ribbon在处理请求超时的时候,Hystirx就出来起作用了,对服务进行熔断,降级处理,进而进入回调方法OrderFeignFallbackFactory,返回友好提示起到服务保护作用。

调用异常:feign.RetryableException: Read timed out executing GET http://nacos-product/getProductInfo/1010110

总结: 通过上述分析,那么在开启Hystrix断路器功能之后就需要对Ribbon进行超时设置了,两者在碰到Feign时,需要协同作战,或者更粗暴点直接不开启Hystrix断路器功能,但是这种方式极不推荐,高并发的项目中服务保护必不可少,所以我们需要将RibbonHystrix结合使用,那么接下来我们需要着重讨论RibbonHystrix如何协同作战。

五、在Feign中禁用Hystrix

1. 禁用Hystrix:
要基于每个客户端禁用Hystrix支持,请创建Feign.Builder具有“原型”范围的香草,这段话摘自官网,例如:
在这里插入图片描述
在这里插入图片描述

import feign.Feign;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class FooConfiguration {
    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder() {
        return Feign.builder();
    }
}

参考官网资料: https://cloud.spring.io/spring-cloud-static/spring-cloud-openfeign/2.2.2.RELEASE/reference/html/#spring-cloud-feign-hystrix

六、同时设置Ribbon超时和Hystrix超时

1. Feign调用时同时设置Ribbon超时和Hystrix超时,application.yml配置如下

server:
  port: 8012
spring:
  application:
    name: nacos-order
  cloud:
    nacos:
      discovery:
        enabled: true
        server-addr: 130.252.102.241:8848
        register-enabled: true
feign:
  hystrix:
    enabled: true
hystrix:
  command:
    default:
      execution:
        timeout:
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 120000
ribbon:
  ReadTimeout: 6000
  ConnectTimeout: 5000

配置截图:
在这里插入图片描述
2. 访问接口: http://130.252.102.241:8012/getProduct/1010110

返回结果: Hello Nacos Discovery 1010110
在这里插入图片描述

七. Ribbon超时值和Hystrix超时值如何设定?

注意: Ribbon超时+Hystrix超时= Feign超时

6.1 Feign超时结论

1. 使用Feign调用接口分两层,ribbon的调用和hystrix的调用,所以ribbon的超时时间和Hystrix的超时时间的结合就是Feign的超时时间
2. 一般情况下 都是 ribbon的超时时间(<)hystrix的超时时间(因为涉及到ribbon的重试机制),如果ribbon的超时时间大于hystrix的超时时间,对于ribbon的重试是没有意义的(hystrix超时熔断了,ribbon无法重试),默认我项目中是没有配置ribbon的重试的,但是需要注意的是无论是否需要配置ribbon超时时的重试次数,hystrix的超时都是需要大于ribbon的超时。

6.2 ribbon的重试机制

为了确保重试机制的正常运作,理论上(以实际情况为准)建议hystrix的超时时间为:(1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout

ribbon:
  OkToRetryOnAllOperations: false #对所有操作请求都进行重试,默认false
  ReadTimeout: 5000   #负载均衡超时时间,默认值5000
  ConnectTimeout: 2000 #ribbon请求连接的超时时间,默认值2000
  MaxAutoRetries: 0     #对当前实例的重试次数,默认0
  MaxAutoRetriesNextServer: 1 #对切换实例的重试次数,默认1
hystrix:
  command:
    default:  #default全局有效,service id指定应用有效
      execution:
        timeout:
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 10000 #断路器超时时间,默认1000ms

如果配置ribbon的重试,hystrix的超时时间要大于ribbon的超时时间,ribbon才会重试,关于Hystrix超时时间值的计算可参考这篇博客:https://blog.csdn.net/wngpenghao/article/details/102399980

八. 最后

关于本篇文章,如果有不对的地方,还请Java界大佬指出,不喜勿喷。

在这里插入图片描述

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读