记一次线上SpringCloud-Feign请求服务超时异常排查
- 2022 年 1 月 13 日
- 筆記
- springboot, 微服务
由于近期线上单量暴涨,第三方反馈部分工单业务存在查询处理失败现象,经排查是当前系统通过FeignClient调用下游系统出现部分超时失败(异常代码贴在下方)。
Caused by: feign.RetryableException: Read timed out executing POST http://xxxx at feign.FeignException.errorExecuting(FeignException.java:84) ~[feign-core-10.1.0.jar!/:na] at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:113) ~[feign-core-10.1.0.jar!/:na] at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:78) ~[feign-core-10.1.0.jar!/:na] at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103) ~[feign-core-10.1.0.jar!/:na] at com.sun.proxy.$Proxy141.creditReportConvert(Unknown Source) ~[na:na] Caused by: java.net.SocketTimeoutException: Read timed out at java.net.SocketInputStream.socketRead0(Native Method) ~[na:1.8.0_121] at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) ~[na:1.8.0_121] at java.net.SocketInputStream.read(SocketInputStream.java:171) ~[na:1.8.0_121] at java.net.SocketInputStream.read(SocketInputStream.java:141) ~[na:1.8.0_121] at java.io.BufferedInputStream.fill(BufferedInputStream.java:246) ~[na:1.8.0_121] at java.io.BufferedInputStream.read1(BufferedInputStream.java:286) ~[na:1.8.0_121] at java.io.BufferedInputStream.read(BufferedInputStream.java:345) ~[na:1.8.0_121] at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:704) ~[na:1.8.0_121] at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647) ~[na:1.8.0_121] at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1569) ~[na:1.8.0_121] at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1474) ~[na:1.8.0_121] at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480) ~[na:1.8.0_121] at feign.Client$Default.convertResponse(Client.java:143) ~[feign-core-10.1.0.jar!/:na] at feign.Client$Default.execute(Client.java:68) ~[feign-core-10.1.0.jar!/:na] at org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.execute(FeignLoadBalancer.java:93) ~[spring-cloud-openfeign-core-2.1.1.RELEASE.jar!/:2.1.1.RELEASE] at org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.execute(FeignLoadBalancer.java:56) ~[spring-cloud-openfeign-core-2.1.1.RELEASE.jar!/:2.1.1.RELEASE] at com.netflix.client.AbstractLoadBalancerAwareClient$1.call(AbstractLoadBalancerAwareClient.java:104) ~[ribbon-loadbalancer-2.3.0.jar!/:2.3.0] at com.netflix.loadbalancer.reactive.LoadBalancerCommand$3$1.call(LoadBalancerCommand.java:303) ~[ribbon-loadbalancer-2.3.0.jar!/:2.3.0]
通过系统慢请求捕捉拦截,发现当前请求仅耗时1031毫秒,就触发Read timed out超时错误,本项目与下游项目均注册在Eureka上面,对这个1秒就超时感到很迷惑,于是开始查阅底层源码之旅。
通过跟踪代码可以在feign.Request内部类Options构造函数默认配置连接超时10秒,读超时60秒。然而本次调用请求仅耗时1秒左右就被掐断连接提示超时错误,初步判断默认这个超时配置并未生效导致。
写到这,咱们先回顾下Feign链路完整调用流程步骤。
可以看出Feign调用分为Hystrix、Ribbon两层,一般高版本的Hystrix默认是关闭的(本项目Hystrix默认是关闭的,那么现在需要去分析Ribbon层调用配置信息)
可以看到RibbonClientConfiguration默认配置的读超时和连接超时时间1000毫秒=1秒,在没有配置超时情况下当前这个超时是符合本次调用超时错误触发。
我们可以在FeignLoadBalancer的execute方法中看到当IClientConfig为空的时候进行覆盖超时时间,默认是取Ribbon的超时时间,并不是Feign Options默认的超时时间。
public FeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig, ServerIntrospector serverIntrospector) { super(lb, clientConfig); this.setRetryHandler(RetryHandler.DEFAULT); this.clientConfig = clientConfig; this.ribbon = RibbonProperties.from(clientConfig); RibbonProperties ribbon = this.ribbon; this.connectTimeout = ribbon.getConnectTimeout(); this.readTimeout = ribbon.getReadTimeout(); this.serverIntrospector = serverIntrospector; }
如果application.properties文件中有配置超时时间,则使用配置的超时时间。否则是取Ribbon默认的超时时间,也就是网上所说的Feign调用服务的默认时长是1秒钟,也就是如果超过1秒没连接上或者超过1秒没响应,那么会相应的报错。
实际业务中服务如果响应时间超过1秒,咱们可以按照实际响应情况给予配置相对应的超时时间,下面分别贴出properties和yml版本的配置
#properties版本 feign.client.config.default.connectTimeout=60000 feign.client.config.default.readTimeout=60000
#yml版本 feign: client: config: default: connectTimeout: 60000 readTimeout: 60000