springCloud中最重要的就是微服务之间的调用,因为网络延迟或者调用超时会直接导致程序异常,因此超时的配置及处理就至关重要。
在开发过程中被调用的微服务打断点发现会又多次重试的情况,测试环境有的请求响应时间过长也会出现多次请求,网上查询了配置试了一下无果,决定自己看看源码。
本人使用的SpringCloud版本是Camden.SR3。微服务间调用其实走的是http请求,debug了一下默认的ReadTimeout时间为5s,ConnectTimeout时间为2s,我使用的是Fegin进行微服务间调用,底层用的还是Ribbon,网上提到的配置如下
ribbon: ReadTimeout:60000 ConnectTimeout:60000 MaxAutoRetries:0 MaxAutoRetriesNextServer:1超时时间是有效的但是重试的次数无效,如果直接使用ribbon应该是有效的。
下面开始测试:
在微服务B中建立测试方法,sleep8s确保请求超时 @PostMapping("/testa") publicIntegertestee(){ try{ Thread.sleep(8000L); }catch(InterruptedExceptione){ e.printStackTrace(); } return9; }在微服务A中使用fegin调用此方法时看到有异常
看到在SynchronousMethodHandler中请求的方法
ObjectexecuteAndDecode(RequestTemplatetemplate)throwsThrowable{ Requestrequest=targetRequest(template); if(logLevel!=Logger.Level.NONE){ logger.logRequest(metadata.configKey(),logLevel,request); } Responseresponse; longstart=System.nanoTime(); try{ response=client.execute(request,options); response.toBuilder().request(request).build(); }catch(IOExceptione){ if(logLevel!=Logger.Level.NONE){ logger.logIOException(metadata.configKey(),logLevel,e,elapsedTime(start)); } //出现异常后抛出RetryableException throwerrorExecuting(request,e); }出现异常后调用throwerrorExecuting(request,e)抛出异常
在调用executeAndDecode的地方catch
@Override publicObjectinvoke(Object[]argv)throwsThrowable{ RequestTemplatetemplate=buildTemplateFromArgs.create(argv); Retryerretryer=this.retryer.clone(); while(true){ try{ returnexecuteAndDecode(template); }catch(RetryableExceptione){ //重试的地方 retryer.continueOrPropagate(e); if(logLevel!=Logger.Level.NONE){ logger.logRetry(metadata.configKey(),logLevel); } continue; } } }retryer.continueOrPropagate(e);这句就是关键继续跟进
publicvoidcontinueOrPropagate(RetryableExceptione){ //maxAttempts是构造方法传进来的大于重试次数抛出异常,否则继续循环执行请求 if(attempt++>=maxAttempts){ throwe; }默认的Retryer构造器
publicDefault(){ this(100,SECONDS.toMillis(1),5); }第一个参数period是请求重试的间隔算法参数,第二个参数maxPeriod是请求间隔最大时间,第三个参数是重试的次数。算法如下:
longnextMaxInterval(){ longinterval=(long)(period*Math.pow(1.5,attempt-1)); returninterval>maxPeriod?maxPeriod:interval; }新建一个配置类
@Configuration publicclassFeginConfig{ @Bean publicRetryerfeginRetryer(){ Retryerretryer=newRetryer.Default(100,SECONDS.toMillis(10),3); returnretryer; } }在feginClient是加入configuration的配置
`` @FeignClient(value="fund-server",fallback=FundClientHystrix.class,configuration=FeginConfig.class) publicinterfaceFundClient重启重试,只调用了一次,Fegin重试次数解决。
我们再看看请求超时这里的参数 @Override publicResponseexecute(Requestrequest,Request.Optionsoptions)throwsIOException{ try{ URIasUri=URI.create(request.url()); StringclientName=asUri.getHost(); URIuriWithoutHost=cleanUrl(request.url(),clientName); FeignLoadBalancer.RibbonRequestribbonRequest=newFeignLoadBalancer.RibbonRequest( this.delegate,request,uriWithoutHost); //请求参数 IClientConfigrequestConfig=getClientConfig(options,clientName); returnlbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse(); } catch(ClientExceptione){ IOExceptionio=findIOException(e); if(io!=null){ throwio; } thrownewRuntimeException(e); } }其中ReadTimeout和ConnectTimeout读取的就是ribbon的配置,再来看一眼
ribbon: ReadTimeout:60000 ConnectTimeout:60000 MaxAutoRetries:0 MaxAutoRetriesNextServer:1如果想覆盖ribbon的超时设置可以在刚刚写的FeginConfig里注入下面的bean
@Bean publicRequest.OptionsfeginOption(){ Request.Optionsoption=newRequest.Options(7000,7000); returnoption; }总结:使用开源的东西在弄不清问题出在哪时最好能看看源码,对原理的实现以及自己的编码思路都有很大的提升。
http://www.jianshu.com/p/767f3c72b547
本文内容总结:
原文链接:https://www.cnblogs.com/zhangjianbin/p/7228396.html