spring cloud 调用feign请求超时 feign.RetryableException: Read timed out executing POST

本文探讨了服务间调用时出现的Feign超时错误,详细解析了错误原因,包括ribbon和hystrix的配置不当,并提供了具体的解决方案,如调整ribbon的超时时间和启用hystrix熔断机制。

问题介绍:

服务之间调用报错超时,截取部分报错,Read timed out executing POST http://******

feign.RetryableException: Read timed out executing POST http://******
at feign.FeignException.errorExecuting(FeignException.java:67)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:104)
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76)
at feign.ReflectiveFeignFeignInvocationHandler.invoke(ReflectiveFeign.java:103)atcom.sun.proxy.FeignInvocationHandler.invoke(ReflectiveFeign.java:103) at com.sun.proxy.FeignInvocationHandler.invoke(ReflectiveFeign.java:103)atcom.sun.proxy.Proxy113.getBaseRow(Unknown Source)
Caused by: java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:170)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:704)
at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1569)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1474)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
at feign.ClientDefault.convertResponse(Client.java:152)atfeign.ClientDefault.convertResponse(Client.java:152) at feign.ClientDefault.convertResponse(Client.java:152)atfeign.ClientDefault.execute(Client.java:74)
原因及解决办法:

明显可以看到是http请求报错超时,feign的调用分两层,ribbon的调用和hystrix的调用,高版本的hystrix默认是关闭的,所以在application.properties配置文件中设置ribbon即可

#请求处理的超时时间
ribbon.ReadTimeout: 120000
#请求连接的超时时间
ribbon.ConnectTimeout: 30000
如果开启hystrix,hystrix的超时报错如下图:

FeignDemo#demo() timed-out and no fallback available。和ribbon超时报错还是有区别的

com.netflix.hystrix.exception.HystrixRuntimeException: FeignDemo#demo() timed-out and no fallback available.
at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:819)
at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:804)
at rx.internal.operators.OperatorOnErrorResumeNextViaFunction4.onError(OperatorOnErrorResumeNextViaFunction.java:140)atrx.internal.operators.OnSubscribeDoOnEach4.onError(OperatorOnErrorResumeNextViaFunction.java:140) at rx.internal.operators.OnSubscribeDoOnEach4.onError(OperatorOnErrorResumeNextViaFunction.java:140)atrx.internal.operators.OnSubscribeDoOnEachDoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at rx.internal.operators.OnSubscribeDoOnEachDoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)atcom.netflix.hystrix.AbstractCommandDoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87) at com.netflix.hystrix.AbstractCommandDoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)atcom.netflix.hystrix.AbstractCommandDeprecatedOnFallbackHookApplication1.onError(AbstractCommand.java:1472)atcom.netflix.hystrix.AbstractCommand1.onError(AbstractCommand.java:1472) at com.netflix.hystrix.AbstractCommand1.onError(AbstractCommand.java:1472)atcom.netflix.hystrix.AbstractCommandFallbackHookApplication1.onError(AbstractCommand.java:1397)atrx.internal.operators.OnSubscribeDoOnEach1.onError(AbstractCommand.java:1397) at rx.internal.operators.OnSubscribeDoOnEach1.onError(AbstractCommand.java:1397)atrx.internal.operators.OnSubscribeDoOnEachDoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at rx.observers.Subscribers$5.onError(Subscribers.java:230)
Caused by: java.util.concurrent.TimeoutException
at com.netflix.hystrix.AbstractCommand.handleTimeoutViaFallback(AbstractCommand.java:997)
at com.netflix.hystrix.AbstractCommand.access$500(AbstractCommand.java:60)
at com.netflix.hystrix.AbstractCommand$12.call(AbstractCommand.java:610)
at com.netflix.hystrix.AbstractCommand$12.call(AbstractCommand.java:601)
解决办法:

feign.hystrix.enabled: true
hystrix 熔断机制
hystrix:
shareSecurityContext: true
command:
default:
circuitBreaker:
sleepWindowInMilliseconds: 100000
forceClosed: true
execution:
isolation:
thread:
timeoutInMilliseconds: 600000

<think>我们正在处理一个关于Feign客户端在熔断降级场景下发生Readtimedout错误的问题。根据用户的问题,我们需要解决FeignRetryableException:Readtimedout异常,并给出熔断降级解决方案。首先,分析问题:Readtimedout通常表示请求处理超时。在Feign客户端中,这可能是由于网络延迟、服务端处理时间过长或资源不足导致的。在熔断降级场景中,我们希望当服务不可用时能够快速失败,避免长时间等待和资源耗尽。参考引用内容:[^1]:介绍了服务熔断是应对微服务雪崩的链路保护机制。[^2]:Hystrix提供熔断机制,快速失败并恢复。[^3]:提到了使用Feign的熔断降级配置(新版使用`circuitbreaker.enabled=true`)。[^4]:详细说明了Feign超时、重试和降级配置,特别是超时时间的设置以及Ribbon重试和Hystrix超时的关系。解决方案思路:1.调整超时设置:确保Ribbon和Hystrix的超时时间设置合理,且Hystrix的超时时间要大于Ribbon的总超时时间,以便Ribbon的重试机制能够生效。2.配置熔断降级:使用Hystrix或SpringCloudCircuitBreaker实现熔断,当错误率达到阈值时快速失败,调用降级方法。3.合理配置重试:根据实际需求配置Ribbon的重试机制,注意非幂等操作的重试风险。具体步骤:一、调整超时时间根据引用[^4],我们需要配置Ribbon和Hystrix的超时时间。在application.yml中配置:```yamlribbon:ConnectTimeout:2000#连接超时时间(毫秒)ReadTimeout:3000#读取超时时间(毫秒)#注意:Ribbon的重试总时间计算公式:(ConnectTimeout+ReadTimeout)*(MaxAutoRetries+1)*(MaxAutoRetriesNextServer+1)#例如,上面的设置和默认重试次数(MaxAutoRetries=0,MaxAutoRetriesNextServer=1)的总时间为:(2000+3000)*(0+1)*(1+1)=10000毫秒#Hystrix的超时时间必须大于Ribbon的总超时时间,否则在Hystrix超时后,Ribbon的重试还没完成就被中断了。hystrix:command:default:execution:isolation:thread:timeoutInMilliseconds:15000#设置Hystrix超时时间大于10000毫秒```二、配置熔断降级1.Feign客户端接口上指定fallback类。2.创建fallback类实现Feign接口,提供降级逻辑。例如:```java@FeignClient(name="service-name",fallback=MyFeignClientFallback.class)publicinterfaceMyFeignClient{@GetMapping("/endpoint")StringcallService();}@ComponentpublicclassMyFeignClientFallbackimplementsMyFeignClient{@OverridepublicStringcallService(){return"fallbackresponse";//降级逻辑}}```三、配置熔断器参数(可选)我们可以调整熔断器的参数,如触发熔断的错误比率、熔断持续时间等。在application.yml中:```yamlhystrix:command:default:circuitBreaker:requestVolumeThreshold:20#熔断触发的最小请求数(默认20)errorThresholdPercentage:50#错误率阈值百分比(默认50%)sleepWindowInMilliseconds:5000#熔断持续时间(毫秒),之后进入半开状态```四、关于重试的注意事项在引用[^4]中提到,Ribbon的重试和Feign的重试有冲突,默认Feign的重试是关闭的。我们通常使用Ribbon的重试。但要注意:-对于非GET请求,如果服务端接口不是幂等的,则不要开启重试(OkToRetryOnAllOperations:false,默认只重试GET请求)。-重试次数需要根据实际情况设置,避免增加系统负担。五、使用新的熔断器(如Resilience4j)如果项目使用SpringCloud2020.0.0及以上版本,Hystrix已被废弃,建议使用SpringCloudCircuitBreakerwithResilience4j。配置方式有所不同:1.添加依赖:spring-cloud-starter-circuitbreaker-resilience4j2.配置Resilience4j的超时和熔断:```yamlresilience4j:timelimiter:instances:myFeignClient:timeoutDuration:10s#超时时间circuitbreaker:instances:myFeignClient:failureRateThreshold:50#失败率阈值百分比minimumNumberOfCalls:20#熔断触发的最小请求数slidingWindowSize:20#滑动窗口大小slidingWindowType:COUNT_BASEDwaitDurationInOpenState:5s#熔断持续时间permittedNumberOfCallsInHalfOpenState:10automaticTransitionFromOpenToHalfOpenEnabled:true```然后在Feign客户端上使用这个实例(通过@FeignClient的fallbackFactory属性指定降级工厂,并利用CircuitBreakerFactory创建熔断器)。总结:解决Readtimedout错误的关键在于合理设置超时时间和重试机制,同时配合熔断降级策略。根据使用的熔断器组件(Hystrix或Resilience4j)进行相应配置。如果问题仍然存在,可能需要检查网络状况、服务提供者的性能或增加服务实例。最后,生成相关问题:1.如何监控Feign客户端的熔断状态?2.除了超时设置,还有哪些方法可以避免Feign客户端的Readtimedout错误?3.在微服务架构中,如何设计一个全局的熔断降级策略?</think>### Feign客户端熔断降级解决Read timed out错误方案 #### 问题分析 `RetryableException: Read timed out`错误通常由以下原因导致: 1. **网络延迟**:服务间通信延迟超过配置阈值 2. **服务端阻塞**:被调用服务处理时间过长 3. **熔断配置不当**:超时时间与重试策略冲突 4. **资源不足**:线程池耗尽或系统负载过高 #### 解决方案(分步实施) ##### 1. 调整超时配置(核心) 在`application.yml`中优化超时参数: ```yaml ribbon: ConnectTimeout: 2000 # 连接超时(ms) ReadTimeout: 5000 # 读超时(ms) MaxAutoRetries: 0 # 单实例重试次数 MaxAutoRetriesNextServer: 1 # 切换实例次数 feign: circuitbreaker: enabled: true # 启用熔断 client: config: default: connectTimeout: 2000 readTimeout: 5000 hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 10000 # 必须大于 (Connect+Read)*(重试次数) ``` > **关键规则**:Hystrix总超时 > Ribbon总耗时 > 计算公式:`Hystrix超时 > (ConnectTimeout + ReadTimeout) * (MaxAutoRetries+1) * (MaxAutoRetriesNextServer+1)` > 示例:`10000 > (2000+5000)*(0+1)*(1+1) = 14000 ❌ 需调整重试或超时值` ##### 2. 配置熔断降级 实现Fallback类处理超时异常: ```java @Component public class UserServiceFallback implements UserServiceClient { @Override public User getUser(String id) { // 返回降级数据 return new User("fallback", "服务暂不可用"); } } // Feign客户端配置 @FeignClient( name = "user-service", fallback = UserServiceFallback.class, configuration = FeignConfig.class ) public interface UserServiceClient { @GetMapping("/users/{id}") User getUser(@PathVariable String id); } ``` ##### 3. 优化熔断策略 ```yaml resilience4j: # 新版推荐替代Hystrix circuitbreaker: instances: user-service: failureRateThreshold: 50 # 触发熔断错误率 minimumNumberOfCalls: 10 # 最小统计请求数 slidingWindowSize: 10 # 统计窗口大小 waitDurationInOpenState: 5s # 熔断持续时间 timelimiter: instances: user-service: timeoutDuration: 10s # 超时中断时间 ``` ##### 4. 关闭冲突重试机制 ```yaml feign: client: retryer: feign.Retryer.NEVER_RETRY # 禁用Feign原生重试 ribbon: OkToRetryOnAllOperations: false # 仅重试GET请求 ``` #### 验证步骤 1. 使用`@PostConstruct`模拟慢服务: ```java @RestController public class UserController { @GetMapping("/users/{id}") public User getUser(@PathVariable String id) throws InterruptedException { Thread.sleep(6000); // 触发超时 return new User(id, "real_data"); } } ``` 2. 观察熔断器状态:访问`/actuator/health`端点 3. 验证降级数据:应返回`"服务暂不可用"` #### 效果保障措施 - **熔断触发**:连续超时后自动短路请求,防止雪崩[^1] - **超时优化**:Ribbon层超时控制TCP连接,Hystrix控制线程中断[^4] - **错误隔离**:线程池隔离避免资源耗尽[^2] - **快速失败**:熔断期间直接返回降级响应,减少等待[^3] > **监控建议**:集成Micrometer+Prometheus监控: > - 熔断器状态转换 > - 请求延迟百分位值 > - 错误率变化趋势
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值