程序员IT@程序员猿媛

springcloud-eureka的重试策略

2018-08-29  本文已影响0人  braveheart075

关于服务的注册和发现,其实中间件很多,之前用的比较多的是consul。后续有时间也会写一些关于consul的文章。好了,言归正传。

eureka-client

eureka-client配置
eureka-client配置
eureka-github

eureka默认使用Ribbon做负载均衡

@RequestMapping(value = "hello",method = RequestMethod.GET)
    public String hello() {
        log.info("you are into this method.....hello.......");
        try {
            TimeUnit.SECONDS.sleep(30);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "hello world.";
    }
you are into this method.....hello.......
@Bean
    @ConditionalOnMissingBean
    public Retryer feignRetryer() {
        return Retryer.NEVER_RETRY;
    }

    @Bean
    @Scope("prototype")
    @ConditionalOnMissingBean
    public Feign.Builder feignBuilder(Retryer retryer) {
        return Feign.builder().retryer(retryer);
    }

    @Bean
    @ConditionalOnMissingBean(FeignLoggerFactory.class)
    public FeignLoggerFactory feignLoggerFactory() {
        return new DefaultFeignLoggerFactory(logger);
    }
at org.springframework.cloud.netflix.feign.ribbon.LoadBalancerFeignClient.execute(LoadBalancerFeignClient.java:63) ~[spring-cloud-netflix-core-1.4.5.RELEASE.jar:1.4.5.RELEASE]
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:97) ~[feign-core-9.5.0.jar:na]
  

feign调用的ribbon的负载均衡。

AbstractLoadBalancerAwareClient.java类:
protected LoadBalancerCommand<T> buildLoadBalancerCommand(final S request, final IClientConfig config) {
        RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request, config);
        LoadBalancerCommand.Builder<T> builder = LoadBalancerCommand.<T>builder()
                .withLoadBalancerContext(this)
                .withRetryHandler(handler)
                .withLoadBalancerURI(request.getUri());
        customizeLoadBalancerCommandBuilder(request, config, builder);
        return builder.build();
    }
@Override
    public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
            RibbonRequest request, IClientConfig requestConfig) {
        if (this.clientConfig.get(CommonClientConfigKey.OkToRetryOnAllOperations,
                false)) {
            return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
                    requestConfig);
        }
        if (!request.toRequest().method().equals("GET")) {
            return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(),
                    requestConfig);
        }
        else {
            return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
                    requestConfig);
        }
    }

这里是实例化类RequestSpecificRetryHandler这个类。这个类我们看下他的构造函数:

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); 
            } 
        }
    }

这里默认的okToRetryOnConnectErrors=true;okToRetryOnAllErrors=true。也就是说在链接错误的时候,会去重试;在所有error发生的时候回去重试。这里重试的handler是defaultLoadBalancerRetryHandler(我们稍后来看)。我们接着跟进去,发现这行代码:

retryNextServer = requestConfig.get(CommonClientConfigKey.MaxAutoRetriesNextServer);

也就是说这个值是从requestConfig里面来的。那么requestConfig是什么?我们返回之前的代码可以看到,他是在LoadBalancerFeignClient类中的方法构造出来的:

IClientConfig getClientConfig(Request.Options options, String clientName) {
        IClientConfig requestConfig;
        if (options == DEFAULT_OPTIONS) {
            requestConfig = this.clientFactory.getClientConfig(clientName);
        } else {
            requestConfig = new FeignOptionsClientConfig(options);
        }
        return requestConfig;
    }

这里我们没有自己配置,所以采用了默认的,也就是说走到了options == DEFAULT_OPTIONS这个分支里。那么如果走else分支,我们是否可以修改这个变量的值,我们看下FeignOptionsClientConfig这个类:

static class FeignOptionsClientConfig extends DefaultClientConfigImpl {

        public FeignOptionsClientConfig(Request.Options options) {
            setProperty(CommonClientConfigKey.ConnectTimeout,
                    options.connectTimeoutMillis());
            setProperty(CommonClientConfigKey.ReadTimeout, options.readTimeoutMillis());
        }

        @Override
        public void loadProperties(String clientName) {

        }

        @Override
        public void loadDefaultValues() {

        }

    }

这个构造函数顶多是重置了connectTimeout和readtimeout的值。Request.Options中也只有着两个值。问题到这里告一段落。我们继续看父类方法:DefaultClientConfigImpl.java

protected Object getProperty(String key) {
        if (enableDynamicProperties) {
            String dynamicValue = null;
            DynamicStringProperty dynamicProperty = dynamicProperties.get(key);
            if (dynamicProperty != null) {
                dynamicValue = dynamicProperty.get();
            }
            if (dynamicValue == null) {
                dynamicValue = DynamicProperty.getInstance(getConfigKey(key)).getString();
                if (dynamicValue == null) {
                    dynamicValue = DynamicProperty.getInstance(getDefaultPropName(key)).getString();
                }
            }
            if (dynamicValue != null) {
                return dynamicValue;
            }
        }
        return properties.get(key);
    }

这个方法是关键。获取properties中的变量。{server_name}.ribbon.{property}。大功告成。

在properties中添加:

ribbon:
      MaxAutoRetriesNextServer: -1

跟进代码,看到这个值已经改变,这个时候看了下代码,对于请求只会发生一次了。

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class EurekaClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaClientApplication.class,args);
    }

    @Bean
    Request.Options options() {
        return new Request.Options(1000*10,40*1000);
    }
}

重写options类。

上一篇下一篇

猜你喜欢

热点阅读