首页 文章资讯内容详情

SpringCloud Fegin超时重试源码

2026-06-01 4 花语

本文内容纲要:

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