做需求的时候,使用Nodejs的request批量请求某一个接口,由于接口超时,出现 ESOCKETTIMEDOUT,程序中断。
1)FeignClient重试机制分析
1、加上 try...catch 之后,程序就不会因为某一条 request 报错而中断。
《SpringCloud | Feign如何整合Ribbon进行负载均衡的?》,所以下面就跳过整合部分,直接分析负载均衡模块。
try{ //这里是你request请求的代码}catch(e){ //这里需要用一些措施记录下失败的数据 //1.将错误请求输出到某一个日志文件中 //2.将错误请求保存到某个数组中,下面通过循环不断重试直至成功 console.log(e);}
执行顺序:
说明:
上述代码是Ribbon判断是否重试的实现,根据我们配置的变量次数,进行判断,有异常则进入异常处理。
时间: 2019-11-02阅读: 156标签: 异常处理
2、在 catch 中将有问题的请求记录下来,方便后面重试。
1)FeignClient 重试机制分析:
为了让程序遇到ESOCKETTIMEDOUT 之后能够继续执行下去,需要对 request 部分加上 try...catch,再catch中记录这条失败请求的信息,后面不断重试。
n(请求总次数)=feign(默认5次) * (MaxAutoRetries+1) * (MaxAutoRetriesNextServer+1)
这里从字面意思可以看出:
10bet,3)FeignClient 和 Ribbon重试区别与联系:
上述代码是对请求类型进行区分,哪些重试,哪些不重试。
区别就在于第二个参数,来看一下第二个参数具体哪里用到了,继续跟进代码如下:
整体的重试机制就是将 LoadBalancerCommand 类中 retryPolicy 的重试实现逻辑,传入RxJava Observable对象的o.retry()方法,该方法接收的参数的就是一个Function:
@OverridepublicObjectinvoke(Object[] argv)throwsThrowable {//生成处理请求模板RequestTemplate template = buildTemplateFromArgs.create(argv);//获取重试配置类Retryer retryer =this.retryer.clone();while(true) {try{returnexecuteAndDecode(template); }catch(RetryableException e) {//在异常里执行是否重试方法retryer.continueOrPropagate(e);if(logLevel != Logger.Level.NONE) { logger.logRetry(metadata.configKey(), logLevel); }continue; } } }
判断是否重试的算法如下:
默认的请求次数为5次,构造方法如下:
publicvoidcontinueOrPropagate(RetryableException e) {//重试次数大于最大请求次数,抛出异常if(attempt++ >= maxAttempts) {throwe; }longinterval;if(e.retryAfter() !=null) { interval = e.retryAfter().getTime() - currentTimeMillis();if(interval > maxPeriod) { interval = maxPeriod; }if(interval <0) {return; } }else{ interval = nextMaxInterval(); }try{ Thread.sleep(interval); }catch(InterruptedException ignored) { Thread.currentThread().interrupt(); } sleptForMillis += interval; }
SpringCloud | FeignClient和Ribbon重试机制区别与联系
上述代码典型的RxJava风格。
/** * @authorzhangshukang */@ConfigurationpublicclassFeignConfig{@BeanRetryer feignRetryer() {returnnewRetryer() {@Override//在这里重写 continueOrPropagate算法,可自定义处理方式。这里直接抛出异常,相当于不重试。publicvoidcontinueOrPropagate(RetryableException e) {throwe; }@OverridepublicRetryerclone() {returnthis; } }; }}
现在在回过头看这两行代码:
+1是代表ribbon本身默认的请求。
publicObservablesubmit(finalServerOperation operation) {finalExecutionInfoContext context =newExecutionInfoContext();if(listenerInvoker !=null) {try{ listenerInvoker.onExecutionStart(); }catch(AbortExecutionException e) {returnObservable.error(e); } }//这两个变量,上面已经提到了,重试机制的关键finalintmaxRetrysSame = retryHandler.getMaxRetriesOnSameServer();finalintmaxRetrysNext = retryHandler.getMaxRetriesOnNextServer();// 利用RxJava生成一个Observable用于后面的回调Observable o =//选择具体的server进行调用(server ==null? selectServer() : Observable.just(server)) .concatMap(newFunc1>() {@Override// Called for each server being selectedpublicObservablecall(Server server) { context.setServer(server);//获取这个server调用监控记录,用于各种统计和LoadBalanceRule的筛选server处理finalServerStats stats = loadBalancerContext.getServerStats(server);//获取本次server调用的回调入口,用于重试同一实例的重试回调Observable o = Observable .just(server) .concatMap(newFunc1>() {@OverridepublicObservablecall(finalServer server) { context.incAttemptCount(); loadBalancerContext.noteOpenConnection(stats);if(listenerInvoker !=null) {try{ listenerInvoker.onStartWithServer(context.toExecutionInfo()); }catch(AbortExecutionException e) {returnObservable.error(e); } }finalStopwatch tracer = loadBalancerContext.getExecuteTracer().start(); ......省略部分代码 } });//设置针对同一实例的重试回调if(maxRetrysSame >0) o = o.retry(retryPolicy(maxRetrysSame,true));returno; } });//设置重试下一个实例的回调 if(maxRetrysNext >0&& server ==null) o = o.retry(retryPolicy(maxRetrysNext,false));//异常回调returno.onErrorResumeNext(newFunc1>() {@OverridepublicObservablecall(Throwable e) {if(context.getAttemptCount() >0) {if(maxRetrysNext >0&& context.getServerAttemptCount() == (maxRetrysNext +1)) { e =newClientException(ClientException.ErrorType.NUMBEROF_RETRIES_NEXTSERVER_EXCEEDED,"Number of retries on next server exceeded max "+ maxRetrysNext +" retries, while making a call for: "+ context.getServer(), e); }elseif(maxRetrysSame >0&& context.getAttemptCount() == (maxRetrysSame +1)) { e =newClientException(ClientException.ErrorType.NUMBEROF_RETRIES_EXEEDED,"Number of retries exceeded max "+ maxRetrysSame +" retries, while making a call for: "+ context.getServer(), e); } }if(listenerInvoker !=null) { listenerInvoker.onExecutionFailed(e, context.toFinalExecutionInfo()); }returnObservable.error(e); } });}
publicTexecuteWithLoadBalancer(finalS request,finalIClientConfig requestConfig)throwsClientException {//获取重试机制配置:RequestSpecificRetryHandler,继续跟进该方法...RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request, requestConfig);//这里很关键,很明显采用了命令模式,ribbon负载均衡的配置在这里传给LoadBalancerCommand类LoadBalancerCommand command = LoadBalancerCommand.builder() .withLoadBalancerContext(this) .withRetryHandler(handler) .withLoadBalancerURI(request.getUri()) .build();try{returncommand.submit(newServerOperation() {@OverridepublicObservablecall(Server server) { URI finalUri = reconstructURIWithServer(server, request.getUri()); S requestForServer = (S) request.replaceUri(finalUri);try{returnObservable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig)); }catch(Exception e) {returnObservable.error(e); } } }) .toBlocking() .single(); }catch(Exception e) { Throwable t = e.getCause();if(tinstanceofClientException) {throw(ClientException) t; }else{thrownewClientException(e); } } }
这里声明一点,关于feignClient如何整合ribbon负载均衡的,之前的博客已经有完整的分析:
publicbooleanisRetriableException(Throwable e,booleansameServer) {//如果手动配置了所有请求都重试,或者get请求时,这里开启重试。if(this.okToRetryOnAllErrors) {returntrue; }elseif(einstanceofClientException) { ClientException ce = (ClientException)e;returnce.getErrorType() == ErrorType.SERVER_THROTTLED?!sameServer:false; }else{returnthis.okToRetryOnConnectErrors &&this.isConnectionException(e); } }
2)再执行上面一行代码,获取执行单个服务的重试逻辑实现,最后再执行具体的业务逻辑。
retrySameServer:重试相同实例,对应MaxAutoRetries
本文由10bet发布于Web前端,转载请注明出处:Nodejs中request出现ESOCKETTIMEDOUT解决方案
关键词: