聊聊openfeign的超时和重试
openfeign是一种声明式的http客户端,它可以方便地集成到springcloud,像调用本地方法一样使用http方式调用远程服务。今天我们来聊一聊feign的超时和重试。
构建环境
注:本文使用的openfeign版本:2.1.0.RELEASE
在pom文件中增加下面配置:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
openfeign默认使用java原生的URLConnection。这里我们要选择一个http的客户端,比如选择apache的,需要在application.properties文件中增加下面这个配置:
feign.httpclient.enabled=true
同时需要在pom文件中引入下面这个jar包:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>9.3.1</version>
</dependency>
我们也可以选择okhttp的,这时需要在application.properties文件中增加下面这个配置:
feign.okhttp.enabled=true
同时需要在pom文件中进入okhttp的jar包:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>10.2.0</version>
</dependency>
在我本地的实验中,有一个服务叫springboot-mybatis,eureka地址是localhost:8889,在application.properties文件中加入下面配置:
eureka.instance.hostname=localhost
eureka.instance.prefer-ip-address=true
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:8889/eureka/
boot启动类加上下面这个注解:
@EnableFeignClients
这样就初步实现了一个openfeign的配置,我在服务端工程(springboot-mybatis)中增加了一个方法,让客户端来调用,代码如下:
@Controller
@RequestMapping("/feign")
public class FeignTestController {
@RequestMapping("/feignReadTimeout")
@ResponseBody
public String getEmployeebyName() throws InterruptedException {
//这里配置10s的超时时间,给后面的实验用
Thread.currentThread().sleep(10000);
return "success";
}
}
在feign客户端的调用代码如下:
@FeignClient("springboot-mybatis")
public interface FeignAsEurekaClient {
@GetMapping("/feign/feignReadTimeout")
String feignReadTimeout();
}
超时配置
注:下面的实验使用的是okhttp来进行的。
上面实现了一个简单的feign使用demo,不过feign的使用还有很多需要注意的地方,这里我们来聊一聊超时。先看第一种情况,feign客户端和服务端都注册在一个eureka的情况。
1.不配置超时时间,默认"读超时60s"
上面的demo我们没有设置超时时间,所以虽然服务端响应延迟10s,请求还是能成功的。
但是上面的"读超时60s"我加了引号,为什么呢?在feign.Request里面有一个内部类,如果不配置超时,外部会调用下面这个构造函数,连接超时10s,读超时60s
public Options() {
this(10 * 1000, 60 * 1000);
}
如果我们没有配置feign超时时间,上面的时间也会被ribbon覆盖?请求连接时间和超时时间,默认为1秒,在RibbonClientConfiguration类定义,被覆盖后也是会读超时的。
覆盖超时时间设置的代码在FeignLoadBalancer,代码如下:
public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
throws IOException {
Request.Options options;
if (configOverride != null) {
RibbonProperties override = RibbonProperties.from(configOverride);
options = new Request.Options(
override.connectTimeout(this.connectTimeout),
override.readTimeout(this.readTimeout));
}
else {
options = new Request.Options(this.connectTimeout, this.readTimeout);
}
Response response = request.client().execute(request.toRequest(), options);
return new RibbonResponse(request.getUri(), response);
}
所以我们加入下面的配置,把ribbon的读超时时间调大,也是可以解决读超时问题的:
ribbon.okhttp.enabled=true
#ribbon.ConnectTimeout=2000
#请求处理的超时时间
ribbon.ReadTimeout=10000
但ribbon是一个做负载均衡的,我们还是给feign定义超时时间比较好。因为feign配置了超时时间后,会最后赋值给Options的超时时间。在FeignClientFactoryBean类的configureUsingProperties方法。
if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
builder.options(new Request.Options(config.getConnectTimeout(), config.getReadTimeout()));
}
2.配置一个默认超时时间
feign.client.config.default.connectTimeout=2000
feign.client.config.default.readTimeout=5000
这里我配置了连接超时是2s,读取超时是5s,这样,上面demo中的请求就失败了,要想成功,readTimeout不能低于10000。
3.为单个服务设置超时时间
如果我们有一个接口超时时间很长,要全局都设置一个这么长的超时时间吗?这样会有问题,一个平时响应很快的接口,如果服务端出故障了,我们应该让它fail-fast。这样我们就需要对慢的接口或服务单独设置超时时间。
对某一个服务设置单独的超时时间,配置如下:
#对单个服务设置超时,会覆盖默认的超时
feign.client.config.springboot-mybatis.connectTimeout=2000
feign.client.config.springboot-mybatis.readTimeout=11000
4.为单个接口设置超时时间
还是上面的问题,一个服务有多个接口,只有一个超级慢,那对整个服务的所有接口设置一个超时时间也会影响其他接口的fail-fast,我们需要对单个接口设置超时时间,就得配合hystrix来配置了,配置如下:
#开启熔断
#feign.hystrix.enabled=true
#开启超时熔断,默认为true,如果为false,则熔断机制只在服务不可用时开启,这个配置不加,使用默认配置
#hystrix.command.default.execution.timeout.enabled=false
#如果上面一个配置是true,设置超时熔断时间,因为openfeign中的熔断时间默认是1s,太短了,我们必须手工设置一个
#hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=15000
#下面的配置为单个接口设置超时时间
#xxx:要设置的某个FeignClient的类名
#yyy:方法名
#zzz:参数,如果是基础类型,就直接填基础类型:String/int;如果是某个对象,就直接填对象的类名
#hystrix.command.xxx#yyy(zzz).execution.isolation.thread.timeoutInMilliseconds=1000
#hystrix.command.FeignAsHttpCient#feignReadTimeout().execution.isolation.thread.timeoutInMilliseconds=23000
这里必须注意一下,ribbon的读超时时间也不能小于接口的返回时间,不然回报ribbon超时,所以ribbon配置如下:
ribbon.ConnectTimeout=2000
ribbon.ReadTimeout=11000
这里我必须说明一下,如果配置了feign的超时时间,并且feign的读超时不够,熔断的超时时间是不起作用的。坑爹啊。原因是什么呢?有待研究。
那不是说openfeign如果给单个服务设置了超时时间,或设置了默认超时时间,就不能给单个响应慢的接口设置超时时间了吗?
下面我们看第二种情况,使用feign作为http客户端来调用外部的服务情况。这种情况的客户端我也给出一个,服务的接口还是刚刚那个:
@FeignClient(name = "feign", url = "http://localhost:8083")
public interface FeignAsHttpCient {
@GetMapping("/feign/feignReadTimeout")
String feignReadTimeout();
}
这里我们需要在application.properties文件中增加下面这个配置就可以了:
hystrix.command.FeignAsHttpCient#feignReadTimeout().execution.isolation.thread.timeoutInMilliseconds=11000
注意,这里feign和ribbon的超时时间对上面这个配置都是不影响的
重试配置
如果不配置,openfeign默认是不重试的,看FeignClientsConfiguration中的代码:
@Bean
@ConditionalOnMissingBean
public Retryer feignRetryer() {
return Retryer.NEVER_RETRY;
}
再看一下Retryer中的NEVER_RETRY定义:
/**
* Implementation that never retries request. It propagates the RetryableException.
*/
Retryer NEVER_RETRY = new Retryer() {
@Override
public void continueOrPropagate(RetryableException e) {
throw e;
}
@Override
public Retryer clone() {
return this;
}
};
下面我给出一个重试的配置:
@Configuration
public class FeignConfigure {
@Bean
public Retryer feignRetryer(){
// period=100 发起当前请求的时间间隔,单位毫秒
// maxPeriod=1000 发起当前请求的最大时间间隔,单位毫秒
// maxAttempts=2 重试次数是1,因为包括第一次,所以我们如果想要重试2次,就需要设置为3
Retryer retryer = new Retryer.Default(100, 1000, 2);
return retryer;
}
}
注意:
hystrix是在ribbon外面,所以hystrix的超时时间不能小于ribbon的(ConnectTimeout + ReadTimeout) * maxAttempts
这样才能保证在Ribbon里的请求还没结束时,Hystrix的熔断时间不会超时
比如下面这个配置:
#feign.client.config.springboot-mybatis.connectTimeout=2000
#feign.client.config.springboot-mybatis.readTimeout=5000
hystrix.command.FeignAsEurekaClient#feignReadTimeout().execution.isolation.thread.timeoutInMilliseconds=15000
ribbon.ReadTimeout=5000
这个时候再执行测试用例FeignAsEurekaClient.feignReadTimeout,打印日志如下,可以看到有2次请求,第2次是重试:
2020-11-10 09:34:42,011 [main] [INFO] org.springframework.test.context.transaction.TransactionContext - Began transaction (1) for test context [DefaultTestContext@587e5365 testClass = TestFeignAsEurekaClient, testInstance = boot.service.TestFeignAsEurekaClient@26b3fd41, testMethod = testFeignReadTimeOut@TestFeignAsEurekaClient, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@22fcf7ab testClass = TestFeignAsEurekaClient, locations = '{}', classes = '{class boot.Application, class boot.Application}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true, server.port=0}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@f4168b8, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@74294adb, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@11c20519, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@25359ed8], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> false]]; transaction manager [org.springframework.jdbc.datasource.DataSourceTransactionManager@56f3f9da]; rollback [true]
2020-11-10 09:34:42,664 [hystrix-springboot-mybatis-1] [DEBUG] boot.feign.FeignAsEurekaClient - [FeignAsEurekaClient#feignReadTimeout] ---> GET http://springboot-mybatis/feign/feignReadTimeout HTTP/1.1
2020-11-10 09:34:42,664 [hystrix-springboot-mybatis-1] [DEBUG] boot.feign.FeignAsEurekaClient - [FeignAsEurekaClient#feignReadTimeout] ---> END HTTP (0-byte body)
2020-11-10 09:34:42,975 [hystrix-springboot-mybatis-1] [INFO] com.netflix.config.ChainedDynamicProperty - Flipping property: springboot-mybatis.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-11-10 09:34:43,002 [hystrix-springboot-mybatis-1] [INFO] com.netflix.util.concurrent.ShutdownEnabledTimer - Shutdown hook installed for: NFLoadBalancer-PingTimer-springboot-mybatis
2020-11-10 09:34:43,003 [hystrix-springboot-mybatis-1] [INFO] com.netflix.loadbalancer.BaseLoadBalancer - Client: springboot-mybatis instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=springboot-mybatis,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2020-11-10 09:34:43,011 [hystrix-springboot-mybatis-1] [INFO] com.netflix.loadbalancer.DynamicServerListLoadBalancer - Using serverListUpdater PollingServerListUpdater
2020-11-10 09:34:43,066 [hystrix-springboot-mybatis-1] [INFO] com.netflix.config.ChainedDynamicProperty - Flipping property: springboot-mybatis.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-11-10 09:34:43,071 [hystrix-springboot-mybatis-1] [INFO] com.netflix.loadbalancer.DynamicServerListLoadBalancer - DynamicServerListLoadBalancer for client springboot-mybatis initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=springboot-mybatis,current list of Servers=[10.192.84.93:8083],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:1; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:10.192.84.93:8083; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@77f618f
2020-11-10 09:34:44,028 [PollingServerListUpdater-0] [INFO] com.netflix.config.ChainedDynamicProperty - Flipping property: springboot-mybatis.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-11-10 09:34:53,266 [hystrix-springboot-mybatis-1] [DEBUG] boot.feign.FeignAsEurekaClient - [FeignAsEurekaClient#feignReadTimeout] <--- ERROR SocketTimeoutException: Read timed out (10600ms)
2020-11-10 09:34:53,267 [hystrix-springboot-mybatis-1] [DEBUG] boot.feign.FeignAsEurekaClient - [FeignAsEurekaClient#feignReadTimeout] 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:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at okio.Okio$2.read(Okio.java:139)
at okio.AsyncTimeout$2.read(AsyncTimeout.java:237)
at okio.RealBufferedSource.indexOf(RealBufferedSource.java:345)
at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:217)
at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:211)
at okhttp3.internal.http1.Http1Codec.readResponseHeaders(Http1Codec.java:189)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:75)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
at okhttp3.RealCall.execute(RealCall.java:69)
at feign.okhttp.OkHttpClient.execute(OkHttpClient.java:167)
at org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.execute(FeignLoadBalancer.java:90)
at org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.execute(FeignLoadBalancer.java:56)
at com.netflix.client.AbstractLoadBalancerAwareClient$1.call(AbstractLoadBalancerAwareClient.java:104)
at com.netflix.loadbalancer.reactive.LoadBalancerCommand$3$1.call(LoadBalancerCommand.java:303)
at com.netflix.loadbalancer.reactive.LoadBalancerCommand$3$1.call(LoadBalancerCommand.java:287)
at rx.internal.util.ScalarSynchronousObservable$3.call(ScalarSynchronousObservable.java:231)
at rx.internal.util.ScalarSynchronousObservable$3.call(ScalarSynchronousObservable.java:228)
at rx.Observable.unsafeSubscribe(Observable.java:10151)
at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.drain(OnSubscribeConcatMap.java:286)
at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.onNext(OnSubscribeConcatMap.java:144)
at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:185)
at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:180)
at rx.Observable.unsafeSubscribe(Observable.java:10151)
at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:94)
at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:42)
at rx.Observable.unsafeSubscribe(Observable.java:10151)
at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber$1.call(OperatorRetryWithPredicate.java:127)
at rx.internal.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.enqueue(TrampolineScheduler.java:73)
at rx.internal.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.schedule(TrampolineScheduler.java:52)
at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber.onNext(OperatorRetryWithPredicate.java:79)
at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber.onNext(OperatorRetryWithPredicate.java:45)
at rx.internal.util.ScalarSynchronousObservable$WeakSingleProducer.request(ScalarSynchronousObservable.java:276)
at rx.Subscriber.setProducer(Subscriber.java:209)
at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:138)
at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:129)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.subscribe(Observable.java:10247)
at rx.Observable.subscribe(Observable.java:10214)
at rx.observables.BlockingObservable.blockForSingle(BlockingObservable.java:444)
at rx.observables.BlockingObservable.single(BlockingObservable.java:341)
at com.netflix.client.AbstractLoadBalancerAwareClient.executeWithLoadBalancer(AbstractLoadBalancerAwareClient.java:112)
at org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient.execute(LoadBalancerFeignClient.java:65)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:108)
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:78)
at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:106)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10151)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
at rx.Observable.unsafeSubscribe(Observable.java:10151)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10151)
at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94)
at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:56)
at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:47)
at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction.call(HystrixContexSchedulerAction.java:69)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
2020-11-10 09:34:53,267 [hystrix-springboot-mybatis-1] [DEBUG] boot.feign.FeignAsEurekaClient - [FeignAsEurekaClient#feignReadTimeout] <--- END ERROR
2020-11-10 09:34:53,267 [hystrix-springboot-mybatis-1] [DEBUG] boot.feign.FeignAsEurekaClient - [FeignAsEurekaClient#feignReadTimeout] ---> RETRYING
2020-11-10 09:34:53,268 [hystrix-springboot-mybatis-1] [DEBUG] boot.feign.FeignAsEurekaClient - [FeignAsEurekaClient#feignReadTimeout] ---> GET http://springboot-mybatis/feign/feignReadTimeout HTTP/1.1
2020-11-10 09:34:53,268 [hystrix-springboot-mybatis-1] [DEBUG] boot.feign.FeignAsEurekaClient - [FeignAsEurekaClient#feignReadTimeout] ---> END HTTP (0-byte body)
2020-11-10 09:34:58,270 [hystrix-springboot-mybatis-1] [WARN] com.netflix.config.sources.URLConfigurationSource - No URLs will be polled as dynamic configuration sources.
2020-11-10 09:34:58,271 [hystrix-springboot-mybatis-1] [INFO] com.netflix.config.sources.URLConfigurationSource - To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.
2020-11-10 09:34:58,272 [hystrix-springboot-mybatis-1] [INFO] com.netflix.config.DynamicPropertyFactory - DynamicPropertyFactory is initialized with configuration sources: com.netflix.config.ConcurrentCompositeConfiguration@5f87f8d3
2020-11-10 09:34:58,274 [hystrix-springboot-mybatis-1] [DEBUG] boot.feign.FeignAsEurekaClient - [FeignAsEurekaClient#feignReadTimeout] <--- ERROR InterruptedIOException: thread interrupted (5006ms)
2020-11-10 09:34:58,275 [hystrix-springboot-mybatis-1] [DEBUG] boot.feign.FeignAsEurekaClient - [FeignAsEurekaClient#feignReadTimeout] java.io.InterruptedIOException: thread interrupted
at okio.Timeout.throwIfReached(Timeout.java:145)
at okio.Okio$1.write(Okio.java:76)
at okio.AsyncTimeout$1.write(AsyncTimeout.java:180)
at okio.RealBufferedSink.flush(RealBufferedSink.java:216)
at okhttp3.internal.http1.Http1Codec.finishRequest(Http1Codec.java:166)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:72)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
而如果把配置改成下面的配置,就只有一次请求了,没有进行重试:
#feign.client.config.springboot-mybatis.connectTimeout=2000
#feign.client.config.springboot-mybatis.readTimeout=5000
hystrix.command.FeignAsEurekaClient#feignReadTimeout().execution.isolation.thread.timeoutInMilliseconds=8000
ribbon.ConnectTimeout=2000
ribbon.ReadTimeout=5000
这里也要注意:如果使用feign作为普通http客户端(不是eureka客户端),是没有重试功能的。
总结
使用openfeign作为http客户端使用起来非常方便,不过也要注意一些复杂场景,比如作为eureka客户端对单个接口设置超时时间,配置比较复杂,需要借助熔断,而且跟整体服务的超时不兼容。
使用openfeign作为普通http客户端,重试功能不能作用。
欢迎关注个人公众号
本文地址:https://blog.csdn.net/zjj2006/article/details/109628470