SpringCloud Fegin超时重试源码
本文内容纲要:
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