Spring Cloud 超时和重试机制是什么

技术Spring Cloud 超时和重试机制是什么这篇文章给大家介绍Spring Cloud 超时和重试机制是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。本文基于Spring Cloud Gr

这篇文章给大家介绍春云超时和重试机制是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

本文基于春云格林威治SR2、Spring Boot 2.1.6。发布

一、Feign的配置

1.1 超时时间

feign:

client:

config:

默认值:

连接超时:2000

读取超时:2000

1.2 重试

春云默认关闭了假装的重试机制

//org。弹簧框架。云。打开假死。feignclients配置

@豆

@ ConditionalOnMissingBean

public retryerfienretyrer(){ 0

返回字符串.从不重试;

}

//feign .Retryer

/**

*实现永不检索请求。它传播了一个异常.

*/

RetryerNEVER _ RETRY=new retryer(){ 0

@覆盖

publication continueopropagate(RetryableExceptione){ 0

投掷;

}

@覆盖

public retryreclone(){ 0

返回此;

}

};如果想要开启的话,就自己声明一个豆

@豆

public retryerfienretyrer(){ 0

返回新字符串.default();

}

二、Ribbon的配置

2.1 超时时间

#色带的超时时间

#如果带状物和假装的超时时间都配置了丝带的配置会被覆盖

ribbon:

ReadTimeout:3000

connect time out :3000

2.2 重试

功能区:

MaxAutoRetries:1 #同一台实例最大重试次数,不包括首次调用

maxautoretriesnextserver :1 #重试负载均衡其他的实例最大重试次数,不包括首次调用

oktoretryonalooperations : false #是否所有操作都重试

三、Hystrix的配置

3.1 超时时间

海斯特:

command:

默认值:

执行:

超时:

启用d :路径

隔离:

sp;    thread:
            timeoutInMilliseconds: 1000

注意Hystrix的超时时间要超过ribbon的重试时间,否则ribbon重试过程中,就会先触发Hystrix的熔断

超时时间计算可以参考zuul中的AbstractRibbonCommand类的getRibbonTimeout()方法,

protected static int getRibbonTimeout(IClientConfig config, String commandKey) {
    int ribbonTimeout;
    // 这是比较异常的情况,不说
    if (config == null) {
        ribbonTimeout = RibbonClientConfiguration.DEFAULT_READ_TIMEOUT + RibbonClientConfiguration.DEFAULT_CONNECT_TIMEOUT;
    } else {
       // 这里获取了四个参数,ReadTimeout,ConnectTimeout,MaxAutoRetries, MaxAutoRetriesNextServer
        int ribbonReadTimeout = getTimeout(config, commandKey, "ReadTimeout",
            IClientConfigKey.Keys.ReadTimeout, RibbonClientConfiguration.DEFAULT_READ_TIMEOUT);
        int ribbonConnectTimeout = getTimeout(config, commandKey, "ConnectTimeout",
            IClientConfigKey.Keys.ConnectTimeout, RibbonClientConfiguration.DEFAULT_CONNECT_TIMEOUT);
        int maxAutoRetries = getTimeout(config, commandKey, "MaxAutoRetries",
            IClientConfigKey.Keys.MaxAutoRetries, DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES);
        int maxAutoRetriesNextServer = getTimeout(config, commandKey, "MaxAutoRetriesNextServer",
            IClientConfigKey.Keys.MaxAutoRetriesNextServer, DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER);
        // ribbonTimeout的计算方法在这里,以上文的设置为例
        // ribbonTimeout = (3000 + 3000) * (1 + 1) * (1 + 1) = 24000(毫秒)
        ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1);
    }
    return ribbonTimeout;
}

四、RestTemplate的配置

4.1 超时时间

RestTemplate默认超时时间是-1,即不会超时,如果想要设置的话,可以这么做

@Bean
@Primary
@LoadBalanced
public RestTemplate lbRestTemplate() {
    SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new   SimpleClientHttpRequestFactory();
    simpleClientHttpRequestFactory.setConnectTimeout(1000);
    simpleClientHttpRequestFactory.setReadTimeout(1000);
    return new RestTemplate(simpleClientHttpRequestFactory);
}

五、Feign调用流程源码分析

5.1 OKToRetryOnAllOperations参数意义

//AbstractLoadBalancerAwareClient.java
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
    LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
	//省略...
}
protected LoadBalancerCommand<T> buildLoadBalancerCommand(final S request, final IClientConfig config) {
    RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request, config);
	//省略...
}
//FeignLoadBalancer.java
//FeignLoadBalancer是AbstractLoadBalancerAwareClient的子类
@Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
    RibbonRequest request, IClientConfig requestConfig) {
    //如果isOkToRetryOnAllOperations参数为true
    if (this.ribbon.isOkToRetryOnAllOperations()) {
        return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), requestConfig);
    }
    if (!request.toRequest().httpMethod().name().equals("GET")) {
        return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(), requestConfig);
    }
    else {
        return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), requestConfig);
    }
}
//RequestSpecificRetryHandler.java
/**
 * okToRetryOnConnectErrors:只对连接错误发起重试
 * okToRetryOnAllErrors:对于所有错误都会发起重试
 */
public RequestSpecificRetryHandler(boolean okToRetryOnConnectErrors, boolean okToRetryOnAllErrors, RetryHandler baseRetryHandler, @Nullable IClientConfig requestConfig) {
    Preconditions.checkNotNull(baseRetryHandler);
    this.okToRetryOnConnectErrors = okToRetryOnConnectErrors;
    this.okToRetryOnAllErrors = okToRetryOnAllErrors;
    this.fallback = baseRetryHandler;
    if (requestConfig != null) {
        if (requestConfig.containsProperty(CommonClientConfigKey.MaxAutoRetries)) {
            retrySameServer = requestConfig.get(CommonClientConfigKey.MaxAutoRetries); 
        }
        if (requestConfig.containsProperty(CommonClientConfigKey.MaxAutoRetriesNextServer)) {
            retryNextServer = requestConfig.get(CommonClientConfigKey.MaxAutoRetriesNextServer); 
        } 
    }
}

可以看到如果设置了isOkToRetryOnAllOperations为true,就会对所有错误发起重试,否则的话就只对连接异常发起重试,判断是否重试的代码如下:

//RequestSpecificRetryHandler.java
@Override
public boolean isRetriableException(Throwable e, boolean sameServer) {
    if (okToRetryOnAllErrors) {
        return true;
    } 
    else if (e instanceof ClientException) {
        ClientException ce = (ClientException) e;
        if (ce.getErrorType() == ClientException.ErrorType.SERVER_THROTTLED) {
            return !sameServer;
        } else {
            return false;
        }
    } 
    else  {
        return okToRetryOnConnectErrors && isConnectionException(e);
    }
}

这里会有一个问题,如果你是新增/修改操作,系统处理时间过长导致超时,也会触发Feign的自动重试,如果你的幂等性做的不好,就会导致很严重的后果。

而如果是连接异常,此时请求还没有发送过去,所以是不会重复执行的。

当然了,在分布式系统中,还是建议做好每个接口的幂等性。

5.2 Feign重试逻辑

//SynchronousMethodHandler.java
@Override
public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    //这里我们假设你开启了Feign的重试,并且使用的是Retryer.Default这个类
    Retryer retryer = this.retryer.clone();
    while (true) {
        try {
            //这里会调用到5.3节executeWithLoadBalancer()方法
            return executeAndDecode(template);
        } catch (RetryableException e) {
            try {
                //在重试次数之内,会等待一段时间返回,继续while循环,否则会抛出异常跳出循环
                retryer.continueOrPropagate(e);
            } catch (RetryableException th) {
                Throwable cause = th.getCause();
                if (propagationPolicy == UNWRAP && cause != null) {
                    throw cause;
                } else {
                    throw th;
                }
            }
            if (logLevel != Logger.Level.NONE) {
                logger.logRetry(metadata.configKey(), logLevel);
            }
            continue;
        }
    }
}

5.3 Ribbon重试逻辑

//AbstractLoadBalancerAwareClient.java
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
    LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
    try {
        return command.submit(
            new ServerOperation<T>() {
                @Override
                public Observable<T> call(Server server) {
                    URI finalUri = reconstructURIWithServer(server, request.getUri());
                    S requestForServer = (S) request.replaceUri(finalUri);
                    try {
                        return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                    } 
                    catch (Exception e) {
                        return Observable.error(e);
                    }
                }
            })
            .toBlocking()
            .single();
    } catch (Exception e) {
        Throwable t = e.getCause();
        if (t instanceof ClientException) {
            throw (ClientException) t;
        } else {
            throw new ClientException(e);
        }
    }
}

上边是调用的入口,下边是重试执行的逻辑,由于的RxJava写的,暂时看不懂,先贴出来日后再说......

//LoadBalancerCommand.java
public Observable<T> submit(final ServerOperation<T> operation) {
    final ExecutionInfoContext context = new ExecutionInfoContext();
    if (listenerInvoker != null) {
        try {
            listenerInvoker.onExecutionStart();
        } catch (AbortExecutionException e) {
            return Observable.error(e);
        }
    }
    final int maxRetrysSame = retryHandler.getMaxRetriesOnSameServer();
    final int maxRetrysNext = retryHandler.getMaxRetriesOnNextServer();
    // Use the load balancer
    Observable<T> o = 
        (server == null ? selectServer() : Observable.just(server))
        .concatMap(new Func1<Server, Observable<T>>() {
            @Override
            // Called for each server being selected
            public Observable<T> call(Server server) {
                context.setServer(server);
                final ServerStats stats = loadBalancerContext.getServerStats(server);
                // Called for each attempt and retry
                Observable<T> o = Observable
                    .just(server)
                    .concatMap(new Func1<Server, Observable<T>>() {
                        @Override
                        public Observable<T> call(final Server server) {
                            context.incAttemptCount();
                            loadBalancerContext.noteOpenConnection(stats);
                            if (listenerInvoker != null) {
                                try {
                                    listenerInvoker.onStartWithServer(context.toExecutionInfo());
                                } catch (AbortExecutionException e) {
                                    return Observable.error(e);
                                }
                            }
                            final Stopwatch tracer = loadBalancerContext.getExecuteTracer().start();
                            return operation.call(server).doOnEach(new Observer<T>() {
                                private T entity;
                                @Override
                                public void onCompleted() {
                                    recordStats(tracer, stats, entity, null);
                                    // TODO: What to do if onNext or onError are never called?
                                }
                                @Override
                                public void onError(Throwable e) {
                                    recordStats(tracer, stats, null, e);
                                    logger.debug("Got error {} when executed on server {}", e, server);
                                    if (listenerInvoker != null) {
                                        listenerInvoker.onExceptionWithServer(e, context.toExecutionInfo());
                                    }
                                }
                                @Override
                                public void onNext(T entity) {
                                    this.entity = entity;
                                    if (listenerInvoker != null) {
                                        listenerInvoker.onExecutionSuccess(entity, context.toExecutionInfo());
                                    }
                                }                            
                                private void recordStats(Stopwatch tracer, ServerStats stats, Object entity, Throwable exception) {
                                    tracer.stop();
                                    loadBalancerContext.noteRequestCompletion(stats, entity, exception, tracer.getDuration(TimeUnit.MILLISECONDS), retryHandler);
                                }
                            });
                        }
                    });
                if (maxRetrysSame > 0) 
                    o = o.retry(retryPolicy(maxRetrysSame, true));
                return o;
            }
        });
    if (maxRetrysNext > 0 && server == null) 
        o = o.retry(retryPolicy(maxRetrysNext, false));
    return o.onErrorResumeNext(new Func1<Throwable, Observable<T>>() {
        @Override
        public Observable<T> call(Throwable e) {
            if (context.getAttemptCount() > 0) {
                if (maxRetrysNext > 0 && context.getServerAttemptCount() == (maxRetrysNext + 1)) {
                    e = new ClientException(ClientException.ErrorType.NUMBEROF_RETRIES_NEXTSERVER_EXCEEDED,
                                            "Number of retries on next server exceeded max " + maxRetrysNext
                                            + " retries, while making a call for: " + context.getServer(), e);
                }
                else if (maxRetrysSame > 0 && context.getAttemptCount() == (maxRetrysSame + 1)) {
                    e = new ClientException(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());
            }
            return Observable.error(e);
        }
    });
}

关于Spring Cloud 超时和重试机制是什么就分享到这里了,希望

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/38495.html

(0)

相关推荐

  • webview怎么改变滑动效果(webview怎么设置旋转)

    技术怎么优雅的对Webview进行截屏怎么优雅的对Webview进行截屏,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。如何优雅的对Webview进行截屏?第

    攻略 2021年12月21日
  • 如何使用node开发一款图集打包工具

    技术如何使用node开发一款图集打包工具这篇文章主要为大家展示了“如何使用node开发一款图集打包工具”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何使用node开发一款图

    攻略 2021年11月30日
  • 然后用AFL开始你的第一次Fuzzing

    技术然后用AFL开始你的第一次Fuzzing然后用AFL开始你的第一次Fuzzing,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。一、前言模糊测试(Fu

    攻略 2021年11月26日
  • Java如何连接COM对象

    技术Java如何连接COM对象这篇文章将为大家详细讲解有关Java如何连接COM对象,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。 问题是在CSDN上一网友提出的将 m$ word 转

    攻略 2021年12月9日
  • 考公务员学什么专业好,考公务员,读什么专业好

    技术考公务员学什么专业好,考公务员,读什么专业好第一名考公务员学什么专业好:经济学
    一般情况下,公务员考试的所有岗位占比中,经济类岗位占比是最多的,考生不仅选择面广,而且可以在各个单位,各个岗位间抉择,可以说在考公务员的

    生活 2021年10月21日
  • 怎么用C语言完整实现2048游戏

    技术怎么用C语言完整实现2048游戏这篇文章主要介绍怎么用C语言完整实现2048游戏,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、游戏思路1、程序开始时出现菜单,让玩家选择开始游戏或者退出游戏

    攻略 2021年11月21日