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覆盖。ribbon请求连接时间和超时时间,默认为1秒?请求连接时间和超时时间,默认为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.FeignAsEurekaClient#feignReadTimeout().execution.isolation.thread.timeoutInMilliseconds=15000
这里必须注意一下,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.ConnectTimeout=2000 ribbon.ReadTimeout=5000
这个时候再执行测试用例FeignAsEurekaClient.feignReadTimeout,打印日志如下,可以看到有2次请求,第2次是重试:
2020-11-10 19: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 19: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 19:34:42,664 [hystrix-springboot-mybatis-1] [DEBUG] boot.feign.FeignAsEurekaClient - [FeignAsEurekaClient#feignReadTimeout] ---> END HTTP (0-byte body) 2020-11-10 19: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 19:34:43,002 [hystrix-springboot-mybatis-1] [INFO] com.netflix.util.concurrent.ShutdownEnabledTimer - Shutdown hook installed for: NFLoadBalancer-PingTimer-springboot-mybatis 2020-11-10 19: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 19:34:43,011 [hystrix-springboot-mybatis-1] [INFO] com.netflix.loadbalancer.DynamicServerListLoadBalancer - Using serverListUpdater PollingServerListUpdater 2020-11-10 19: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 19: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 19: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 19:34:53,266 [hystrix-springboot-mybatis-1] [DEBUG] boot.feign.FeignAsEurekaClient - [FeignAsEurekaClient#feignReadTimeout] <--- ERROR SocketTimeoutException: Read timed out (10600ms) 2020-11-10 19: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 19:34:53,267 [hystrix-springboot-mybatis-1] [DEBUG] boot.feign.FeignAsEurekaClient - [FeignAsEurekaClient#feignReadTimeout] <--- END ERROR 2020-11-10 19:34:53,267 [hystrix-springboot-mybatis-1] [DEBUG] boot.feign.FeignAsEurekaClient - [FeignAsEurekaClient#feignReadTimeout] ---> RETRYING 2020-11-10 19: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 19:34:53,268 [hystrix-springboot-mybatis-1] [DEBUG] boot.feign.FeignAsEurekaClient - [FeignAsEurekaClient#feignReadTimeout] ---> END HTTP (0-byte body) 2020-11-10 19: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 19: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 19: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 19:34:58,274 [hystrix-springboot-mybatis-1] [DEBUG] boot.feign.FeignAsEurekaClient - [FeignAsEurekaClient#feignReadTimeout] <--- ERROR InterruptedIOException: thread interrupted (5006ms) 2020-11-10 19: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客户端,重试功能不能作用。