精华内容
下载资源
问答
  • Zuul 重试

    千次阅读 2018-10-30 15:57:13
      在生产环境中,总会因为种种原因(无论是网络、性能等)导致档次请求的失败,这时候就需要使用到重试了,Zuul 可以结合 Ribbon(默认集成)进行重试。 引入依赖 <dependency> &...

      在生产环境中,总会因为种种原因(无论是网络、性能等)导致档次请求的失败,这时候就需要使用到重试了,Zuul 可以结合 Ribbon(默认集成)进行重试。

    1. 引入依赖
    <dependency>
        <groupId>org.springframework.retry</groupId>
        <artifactId>spring-retry</artifactId>
    </dependency>
    
    1. 配置
    spring:
      cloud:
        loadbalancer:
          retry:
            # 重试开关,默认:true
            enabled: true
    
    ### Ribbon 配置
    ribbon:
      # http建立socket超时时间,毫秒
      ConnectTimeout: 2000
      # http读取响应socket超时时间
      ReadTimeout: 8000
      # 同一台实例最大重试次数,不包括首次调用
      MaxAutoRetries: 0
      # 重试负载均衡其他的实例最大重试次数,不包括首次server
      MaxAutoRetriesNextServer: 1
      # 是否所有操作都重试,POST请求注意多次提交错误。
      # 默认false,设定为false的话,只有get请求会重试
      OkToRetryOnAllOperations: true
    
    ### Hystrix 配置
    hystrix:
      # 这样将会自动配置一个 Hystrix 并发策略插件的 hook,这个 hook 会将 SecurityContext 从主线程传输到 Hystrix 的命令。
      # 因为 Hystrix 不允许注册多个 Hystrix 策略,所以可以声明 HystrixConcurrencyStrategy
      # 为一个 Spring bean 来实现扩展。Spring Cloud 会在 Spring 的上下文中查找你的实现,并将其包装在自己的插件中。
      shareSecurityContext: true
      command:
        default:
          # 断路器配置
          circuitBreaker:
            # 当在配置时间窗口内达到此数量的失败后,进行断路。默认:20个
            requestVolumeThreshold: 20
            # 出错百分比阈值,当达到此阈值后,开始断路。默认:50%
            errorThresholdpercentage: 50
            # 触发短路的时间值,当该值设为5000时,则当触发 circuit break 后的5000毫秒内都会拒绝request
            # 也就是5000毫秒后才会关闭circuit。默认:5000
            sleepWindowInMilliseconds: 5000
            # 强制打开断路器,如果打开这个开关,那么拒绝所有request,默认false
            forceOpen: false
            # 强制关闭断路器 如果这个开关打开,circuit将一直关闭且忽略,默认false
            forceClosed: false
          execution:
            # 熔断器配置
            isolation:
              thread:
                # 熔断器超时时间,默认:1000/毫秒
                timeoutInMilliseconds: 20000
                # 超时时是否立马中断
                interruptOnTimeout: true
              semaphore:
                # 信号量请求数,当设置为信号量隔离策略时,设置最大允许的请求数
                maxConcurrentRequests: 10
      #        timeout:
      #          # 禁用熔断器超时时间,不推荐
      #          enabled: false
      threadpool:
        defalut:
          # 当使用线程隔离策略时,线程池的核心大小
          coreSize: 10
          # 当 Hystrix 隔离策略为线程池隔离模式时,最大线程池大小的配置,在 `1.5.9` 版本中还需要配置 `allowMaximumSizeToDivergeFromCoreSize` 为 true
          maximumSize: 10
          # 此属性语序配置的 maximumSize 生效
          allowMaximumSizeToDivergeFromCoreSize: true
    
    ### 网关配置
    zuul:
      routes:
        demo-order:
          path: /do/**
          serviceId: demo-order
          stripPrefix: true
          sensitiveHeaders: Cookie,Set-Cookie,Authorization
      # 开启重试机制,结合 Ribbon(默认集成),只需配置即可,慎用,有些接口要考虑到幂等性,D 版之后默认:false
      retryable: true
    

      ⬆️上方的配置,首先配置了 Ribbon 的重试、超时机制,下面接着配置了断路器的参数。

    源码:https://github.com/SlowSlicing/demo-spring-cloud-finchley/tree/Zuul重试

    展开全文
  • 本篇文章主要介绍了详解Spring Cloud Zuul重试机制探秘,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • zuul重试配置

    2018-03-01 13:45:00
    #retry#该参数用来开启重试机制spring.cloud.loadbalancer.retry.enabled=true#断路器的超时时间,断路器的超时时间需要大于ribbon的超时时间,不然不会触发重试。hystrix.command.default.execution.isolation....

    #retry
    #该参数用来开启重试机制
    spring.cloud.loadbalancer.retry.enabled=true
    #断路器的超时时间,断路器的超时时间需要大于ribbon的超时时间,不然不会触发重试。
    hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
    #ribbon请求连接的超时时间
    ribbon.ConnectTimeout=250
    #请求处理的超时时间
    ribbon.ReadTimeout=1000
    #对所有操作请求都进行重试
    ribbon.OkToRetryOnAllOperations=true
    #对当前实例的重试次数
    ribbon.MaxAutoRetries=1
    #对下个实例的重试次数
    ribbon.MaxAutoRetriesNextServer=1

    转载于:https://www.cnblogs.com/myf008/p/8488053.html

    展开全文
  • 怎么开启zuul重试机制 Edgware.RC1版本的优化 开启Zuul的功能 首先如何使用spring cloud zuul完成路由转发的功能,这个问题很简单,只需要进行如下准备工作即可: 注册中心(Eureka Server) zuul(同时也是...

    简介

    本文章对应spring cloud的版本为(Dalston.SR4),具体内容如下:

    • 开启Zuul功能
    • 通过源码了解Zuul的一次转发
    • 怎么开启zuul的重试机制
    • Edgware.RC1版本的优化

    开启Zuul的功能

    首先如何使用spring cloud zuul完成路由转发的功能,这个问题很简单,只需要进行如下准备工作即可:

    • 注册中心(Eureka Server)
    • zuul(同时也是Eureka Client)
    • 应用服务(同时也是Eureka Client)

    我们希望zuul和后端的应用服务同时都注册到Eureka Server上,当我们访问Zuul的某一个地址时,对应其实访问的是后端应用的某个地址,从而从这个地址返回一段内容,并展现到浏览器上。

    注册中心(Eureka Server)

    创建一个Eureka Server只需要在主函数上添加@EnableEurekaServer

    @EnableEurekaServer
    @RestController
    @SpringBootApplication
    public class EurekaServerApplication {
    
       public static void main(String[] args) {
          SpringApplication.run(EurekaServerApplication.class, args);
       }
    }

     在properties文件进行简单配置

    server.port=8761
    eureka.client.register-with-eureka=false
    eureka.client.fetch-registry=false

    Zuul

    主函数添加@EnableZuulProxy注解(因为集成Eureka,需要另外添加@EnableDiscoveryClient注解)。

    @EnableZuulProxy
    @EnableDiscoveryClient
    @SpringBootApplication
    public class ZuulDemoApplication {
      	/**
      	 * 省略代码...
      	 */
    }

     配置properties文件

    server.port=8081
    spring.application.name=ZUUL-CLIENT
    zuul.routes.api-a.serviceId=EUREKA-CLIENT
    zuul.routes.api-a.path=/api-a/**
    eureka.client.service-url.defaultZone=http://localhost:8761/eureka
    

    应用服务 

    @RestController
    @EnableEurekaClient
    @SpringBootApplication
    public class EurekaClientApplication {
    
       public static void main(String[] args) {
          SpringApplication.run(EurekaClientApplication.class, args);
       }
    
       @RequestMapping(value = "/hello")
       public String index() {
          return "hello spring...";
       }
    }

     配置properties文件

    spring.application.name=EUREKA-CLIENT
    eureka.client.service-url.defaultZone=http://localhost:8761/eureka

    三个工程全部启动,这时当我们访问localhost:8081/api-a/hello时,你会看到浏览器输出的内容是hello spring...

    通过源码了解Zuul的一次转发

    接下来我们通过源码层面来了解下,一次转发内部都做了哪些事情。

    首先我们查看Zuul的配置类ZuulProxyAutoConfiguration在这个类中有一项工作是初始化Zuul默认自带的Filter,其中有一个Filter很重要,它就是RibbonRoutingFilter。它主要是完成请求的路由转发。接下来我们看下他的run方法

    @Override
    public Object run() {
       RequestContext context = RequestContext.getCurrentContext();
       try {
          RibbonCommandContext commandContext = buildCommandContext(context);
          ClientHttpResponse response = forward(commandContext);
          setResponse(response);
          return response;
       }
       catch (ZuulException ex) {
          throw new ZuulRuntimeException(ex);
       }
       catch (Exception ex) {
          throw new ZuulRuntimeException(ex);
       }
    }

    可以看到进行转发的方法是forward,我们进一步查看这个方法,具体内容如下:
    省略部分代码 

    protected ClientHttpResponse forward(RibbonCommandContext context) throws Exception {
       RibbonCommand command = this.ribbonCommandFactory.create(context);
       try {
          ClientHttpResponse response = command.execute();
          return response;
       }
       catch (HystrixRuntimeException ex) {
          return handleException(info, ex);
       }
    }

    ribbonCommandFactory指的是HttpClientRibbonCommandFactory这个类是在RibbonCommandFactoryConfiguration完成初始化的(触发RibbonCommandFactoryConfiguration的加载动作是利用ZuulProxyAutoConfiguration类上面的@Import标签),具体代码如下: 

    @Configuration
    @ConditionalOnRibbonHttpClient
    protected static class HttpClientRibbonConfiguration {
    
       @Autowired(required = false)
       private Set<ZuulFallbackProvider> zuulFallbackProviders = Collections.emptySet();
    
       @Bean
       @ConditionalOnMissingBean
       public RibbonCommandFactory<?> ribbonCommandFactory(
             SpringClientFactory clientFactory, ZuulProperties zuulProperties) {
          return new HttpClientRibbonCommandFactory(clientFactory, zuulProperties, zuulFallbackProviders);
       }
    }

    知道了这个ribbonCommandFactory具体的实现类(HttpClientRibbonCommandFactory),接下来我们看看它的create方法具体做了那些事情 

    @Override
    public HttpClientRibbonCommand create(final RibbonCommandContext context) {
       ZuulFallbackProvider zuulFallbackProvider = getFallbackProvider(context.getServiceId());
       final String serviceId = context.getServiceId();
       final RibbonLoadBalancingHttpClient client = this.clientFactory.getClient(
             serviceId, RibbonLoadBalancingHttpClient.class);
       client.setLoadBalancer(this.clientFactory.getLoadBalancer(serviceId));
    
       return new HttpClientRibbonCommand(serviceId, client, context, zuulProperties, zuulFallbackProvider,
             clientFactory.getClientConfig(serviceId));
    }

    这个方法按照我的理解主要做了以下几件事情: 

    @Override
    public HttpClientRibbonCommand create(final RibbonCommandContext context) {
       /**
        *获取所有ZuulFallbackProvider,即当Zuul
        *调用失败后的降级方法
        */
       ZuulFallbackProvider = xxxxx
       /**
        *创建处理请求转发类,该类会利用
        *Apache的Http client进行请求的转发
        */
       RibbonLoadBalancingHttpClient = xxxxx
       
       /**
        *将降级方法、处理请求转发类、以及其他一些内容
        *包装成HttpClientRibbonCommand(这个类继承了HystrixCommand)
        */
       return new HttpClientRibbonCommand(xxxxx);
    }

    到这里我们很清楚的知道了RibbonRoutingFilter类的forward
    方法中RibbonCommand command = this.ribbonCommandFactory.create(context);这一行代码都做了哪些内容.

    接下来调用的是command.execute();方法,通过刚刚的分析我们知道了command其实指的是HttpClientRibbonCommand,同时我们也知道HttpClientRibbonCommand继承了HystrixCommand所以当执行command.execute();时其实执行的是HttpClientRibbonCommandrun方法。查看源码我们并没有发现run方法,但是我们发现HttpClientRibbonCommand直接继承了AbstractRibbonCommand。所以其实执行的是AbstractRibbonCommand的run方法,接下来我们看看run方法里面都做了哪些事情:

    @Override
    protected ClientHttpResponse run() throws Exception {
       final RequestContext context = RequestContext.getCurrentContext();
       RQ request = createRequest();
       RS response = this.client.executeWithLoadBalancer(request, config);
       context.set("ribbonResponse", response);
       if (this.isResponseTimedOut()) {
          if (response != null) {
             response.close();
          }
       }
       return new RibbonHttpResponse(response);
    }

    可以看到在run方法中会调用client的executeWithLoadBalancer方法,通过上面介绍我们知道client指的是RibbonLoadBalancingHttpClient,而RibbonLoadBalancingHttpClient里面并没有executeWithLoadBalancer方法。(这里面会最终调用它的父类AbstractLoadBalancerAwareClientexecuteWithLoadBalancer方法。)

    具体代码如下:

    public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
    	/**
    	 * 创建一个RetryHandler,这个很重要它是用来
    	 * 决定利用RxJava的Observable是否进行重试的标准。
    	 */
        RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request, requestConfig);
        /**
         * 创建一个LoadBalancerCommand,这个类用来创建Observable
         * 以及根据RetryHandler来判断是否进行重试操作。
         */
        LoadBalancerCommand<T> command = LoadBalancerCommand.<T>builder()
                .withLoadBalancerContext(this)
                .withRetryHandler(handler)
                .withLoadBalancerURI(request.getUri())
                .build();
    
        try {
        	/**
        	 *command.submit()方法主要是创建了一个Observable(RxJava)
        	 *并且为这个Observable设置了重试次数,这个Observable最终
        	 *会回调AbstractLoadBalancerAwareClient.this.execute()
        	 *方法。
        	 */
            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);
            }
        }
        
    }

    下面针对于每一块内容做详细说明:
    首先getRequestSpecificRetryHandler(request, requestConfig);这个方法其实调用的是RibbonLoadBalancingHttpClientgetRequestSpecificRetryHandler方法,这个方法主要是返回一个RequestSpecificRetryHandler 

    @Override
    public RequestSpecificRetryHandler getRequestSpecificRetryHandler(RibbonApacheHttpRequest request, IClientConfig requestConfig) {
    	/**
    	 *这个很关键,请注意该类构造器中的前两个参数的值
    	 *正因为一开始我也忽略了这两个值,所以后续给我造
    	 *成一定的干扰。
    	 */
       return new RequestSpecificRetryHandler(false, false,
             RetryHandler.DEFAULT, requestConfig);
    }

    接下来创建LoadBalancerCommand并将上一步获得的RequestSpecificRetryHandler作为参数内容。
    最后调用LoadBalancerCommandsubmit方法。该方法内容太长具体代码细节就不在这里贴出了,按照我个人的理解,只贴出相应的伪代码: 

    public Observable<T> submit(final ServerOperation<T> operation) {
    	//相同server的重试次数(去除首次请求)
        final int maxRetrysSame = retryHandler.getMaxRetriesOnSameServer();
        //集群内其他Server的重试个数
        final int maxRetrysNext = retryHandler.getMaxRetriesOnNextServer();
        /**
         *创建一个Observable(RxJava),selectServer()方法是
         *利用Ribbon选择一个Server,并将其包装成Observable
         */
        Observable<T> o = selectServer().concatMap(new Func1<Server, Observable<T>>() { 
            @Override
            public Observable<T> call(final Server server) {
            	/**
            	 *这里会回调submit方法入参ServerOperation类的call方法,
            	 */
      			return operation.call(server).doOnEach(new Observer<T>() {}
    		}
        }
        if (maxRetrysSame > 0) 
        	o = o.retry(retryPolicy(maxRetrysSame, true));
            
        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) {
     	    	/**
     	    	 *转发请求失败时,会进入此方法。通过此方法进行判断
     	    	 *是否超过重试次数maxRetrysSame、maxRetrysNext。
     	    	 */
     	    }
        });
    }

    operation.call()方法最终会调用RibbonLoadBalancingHttpClientexecute方法,该方法内容如下: 

    @Override
    public RibbonApacheHttpResponse execute(RibbonApacheHttpRequest request,
          final IClientConfig configOverride) throws Exception {
       /**
        * 组装参数(RequestConfig)
        */
       final RequestConfig.Builder builder = RequestConfig.custom();
       IClientConfig config = configOverride != null ? configOverride : this.config;
       builder.setConnectTimeout(config.get(
             CommonClientConfigKey.ConnectTimeout, this.connectTimeout));
       builder.setSocketTimeout(config.get(
             CommonClientConfigKey.ReadTimeout, this.readTimeout));
       builder.setRedirectsEnabled(config.get(
             CommonClientConfigKey.FollowRedirects, this.followRedirects));
    
       final RequestConfig requestConfig = builder.build();
       if (isSecure(configOverride)) {
          final URI secureUri = UriComponentsBuilder.fromUri(request.getUri())
                .scheme("https").build().toUri();
          request = request.withNewUri(secureUri);
       }
       final HttpUriRequest httpUriRequest = request.toRequest(requestConfig);
       /**
        * 发送转发请求
        */
       final HttpResponse httpResponse = this.delegate.execute(httpUriRequest);
       /**
        * 返回结果
        */
       return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
    }

    可以看到上面方法主要做的就是组装请求参数(包括各种超时时间),然后发起转发请求,最终获取相应结果。

    说到这里,zuul转发一次请求的基本原理就说完了。让我们再回顾下整个流程。

    • zuul的转发是通过RibbonRoutingFilter这个Filter进行操作的。
    • 在转发之前,zuul利用Hystrix将此次转发请求包装成一个HystrixCommand,正应为这样才使得zuul具有了降级(Fallback)的功能,同时HystrixCommand是具备超时时间的(默认是1s)。而且Zuul默认采用的隔离级别是信号量模式。
    • 在HystrixCommand内部zuul再次将请求包装成一个Observable,(有关RxJava的知识请参照其官方文档)。并且为Observable设置了重试次数。

    事实真的是这样吗?当我看到源码中为Observable设置重试次数的时候,我以为这就是zuul的重试逻辑。遗憾的是我的想法是错误的。还记得上面我说的getRequestSpecificRetryHandler(request, requestConfig);这个方法吗?(不记得的同学可以回过头来再看下),这个方法返回的是RequestSpecificRetryHandler这个类,而且在创建该类时,构造器的前两个参数都为false。(这一点非常重要)。这两个参数分别是okToRetryOnConnectErrorsokToRetryOnAllErrors

    我原本的想法是这个请求被包装成Observable,如果这次请求因为超时出现异常或者其他异常,这样就会触发Observable的重试机制(RxJava),但是事实并非如此,为什么呢?原因就是上面的那两个参数,当出现了超时异常的时候,在触发重试机制之前会调用RequestSpecificRetryHandlerisRetriableException()方法,该方法的作用是用来判断是否执行重试动作,具体代码如下:

    @Override
    public boolean isRetriableException(Throwable e, boolean sameServer) {
        //此时该值为false
        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  {
            //此时该值为false
            return okToRetryOnConnectErrors && isConnectionException(e);
        }
    }

    说道这里zuul转发一次请求的基本原理大概了解了,同时也验证了一个事实就是实现zuul进行重试的逻辑并不是Observable的重试机制。那么问题来了?是什么使zuul具有重试功能的呢?

    怎么开启zuul的重试机制

    开启Zuul重试的功能在原有的配置基础上需要额外进行以下设置:

    • 在pom中添加spring-retry的依赖(maven工程)
    • 设置zuul.retryable=true(该参数默认为false)

    具体properties文件内容如下:

    server.port=8081
    spring.application.name=ZUUL-CLIENT
    #路由信息
    zuul.routes.api-a.serviceId=EUREKA-CLIENT
    zuul.routes.api-a.path=/api-a/**
    #是否开启重试功能
    zuul.retryable=true
    #同一个Server重试的次数(除去首次)
    ribbon.MaxAutoRetries=3
    #切换相同Server的次数
    ribbon.MaxAutoRetriesNextServer=0
    eureka.client.service-url.defaultZone=http://localhost:8761/eureka
    

    为了模拟出Zuul重试的功能,需要对后端应用服务进行改造,改造后的内容如下: 

    @RequestMapping(value = "/hello")
    public String index() {
       System.out.println("request is coming...");
       try {
          Thread.sleep(100000);
       } catch (InterruptedException e) {
          System.out.println("线程被打断... " + e.getMessage());
       }
       return "hello spring ...";
    }

    通过使用Thread.sleep(100000)达到Zuul转发超时情况(Zuul默认连接超时未2s、read超时时间为5s),从而触发Zuul的重试功能。这时候在此访问localhost:8081/api-a/hello时,查看应用服务后台,会发现最终打印三次"request is coming..."

    通过现象看本质,接下来简单介绍下Zuul重试的原理。首先如果你工程classpath中存在spring-retry,那么zuul在初始化的时候就不会创建RibbonLoadBalancingHttpClient而是创建RetryableRibbonLoadBalancingHttpClient具体源代码如下:

    @ConditionalOnClass(name = "org.apache.http.client.HttpClient")
    @ConditionalOnProperty(name = "ribbon.httpclient.enabled", matchIfMissing = true)
    public class HttpClientRibbonConfiguration {
       @Value("${ribbon.client.name}")
       private String name = "client";
    
       @Bean
       @ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
       @ConditionalOnMissingClass(value = "org.springframework.retry.support.RetryTemplate")
       public RibbonLoadBalancingHttpClient ribbonLoadBalancingHttpClient(
             IClientConfig config, ServerIntrospector serverIntrospector,
             ILoadBalancer loadBalancer, RetryHandler retryHandler) {
          RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient(
                config, serverIntrospector);
          client.setLoadBalancer(loadBalancer);
          client.setRetryHandler(retryHandler);
          Monitors.registerObject("Client_" + this.name, client);
          return client;
       }
    
       @Bean
       @ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
       @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
       public RetryableRibbonLoadBalancingHttpClient retryableRibbonLoadBalancingHttpClient(
             IClientConfig config, ServerIntrospector serverIntrospector,
             ILoadBalancer loadBalancer, RetryHandler retryHandler,
             LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory) {
          RetryableRibbonLoadBalancingHttpClient client = new RetryableRibbonLoadBalancingHttpClient(
                config, serverIntrospector, loadBalancedRetryPolicyFactory);
          client.setLoadBalancer(loadBalancer);
          client.setRetryHandler(retryHandler);
          Monitors.registerObject("Client_" + this.name, client);
          return client;
       }
    }

    所以请求到来需要转发的时候(AbstractLoadBalancerAwareClient类中executeWithLoadBalancer方法会调用AbstractLoadBalancerAwareClient.this.execute())其实调用的是RetryableRibbonLoadBalancingHttpClientexecute方法(而不是没有重试时候RibbonLoadBalancingHttpClientexecute方法),源码内容如下: 

    @Override
    public RibbonApacheHttpResponse execute(final RibbonApacheHttpRequest request, final IClientConfig configOverride) throws Exception {
       final RequestConfig.Builder builder = RequestConfig.custom();
       IClientConfig config = configOverride != null ? configOverride : this.config;
       builder.setConnectTimeout(config.get(
             CommonClientConfigKey.ConnectTimeout, this.connectTimeout));
       builder.setSocketTimeout(config.get(
             CommonClientConfigKey.ReadTimeout, this.readTimeout));
       builder.setRedirectsEnabled(config.get(
             CommonClientConfigKey.FollowRedirects, this.followRedirects));
    
       final RequestConfig requestConfig = builder.build();
       final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
       RetryCallback retryCallback = new RetryCallback() {
          @Override
          public RibbonApacheHttpResponse doWithRetry(RetryContext context) throws Exception {
             //on retries the policy will choose the server and set it in the context
             //extract the server and update the request being made
             RibbonApacheHttpRequest newRequest = request;
             if(context instanceof LoadBalancedRetryContext) {
                ServiceInstance service = ((LoadBalancedRetryContext)context).getServiceInstance();
                if(service != null) {
                   //Reconstruct the request URI using the host and port set in the retry context
                   newRequest = newRequest.withNewUri(new URI(service.getUri().getScheme(),
                         newRequest.getURI().getUserInfo(), service.getHost(), service.getPort(),
                         newRequest.getURI().getPath(), newRequest.getURI().getQuery(),
                         newRequest.getURI().getFragment()));
                }
             }
             if (isSecure(configOverride)) {
                final URI secureUri = UriComponentsBuilder.fromUri(newRequest.getUri())
                      .scheme("https").build().toUri();
                newRequest = newRequest.withNewUri(secureUri);
             }
             HttpUriRequest httpUriRequest = newRequest.toRequest(requestConfig);
             final HttpResponse httpResponse = RetryableRibbonLoadBalancingHttpClient.this.delegate.execute(httpUriRequest);
             if(retryPolicy.retryableStatusCode(httpResponse.getStatusLine().getStatusCode())) {
                if(CloseableHttpResponse.class.isInstance(httpResponse)) {
                   ((CloseableHttpResponse)httpResponse).close();
                }
                throw new RetryableStatusCodeException(RetryableRibbonLoadBalancingHttpClient.this.clientName,
                      httpResponse.getStatusLine().getStatusCode());
             }
             return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
          }
       };
       return this.executeWithRetry(request, retryPolicy, retryCallback);
    }

    executeWithRetry方法内容如下: 

    private RibbonApacheHttpResponse executeWithRetry(RibbonApacheHttpRequest request, LoadBalancedRetryPolicy retryPolicy, RetryCallback<RibbonApacheHttpResponse, IOException> callback) throws Exception {
       RetryTemplate retryTemplate = new RetryTemplate();
       boolean retryable = request.getContext() == null ? true :
             BooleanUtils.toBooleanDefaultIfNull(request.getContext().getRetryable(), true);
       retryTemplate.setRetryPolicy(retryPolicy == null || !retryable ? new NeverRetryPolicy()
             : new RetryPolicy(request, retryPolicy, this, this.getClientName()));
       return retryTemplate.execute(callback);
    }

    按照我的理解,主要逻辑如下: 

    @Override
    public RibbonApacheHttpResponse execute(final RibbonApacheHttpRequest request, final IClientConfig configOverride) throws Exception {
       /**
        *创建RequestConfig(请求信息)
        */
       final RequestConfig requestConfig = builder.build();
       final LoadBalancedRetryPolicy retryPolicy =            	loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
       /**
        * 创建RetryCallbck的实现类,用来完成重试逻辑
        */
       RetryCallback retryCallback = new RetryCallback() {};
       
       //创建Spring-retry的模板类,RetryTemplate。
       RetryTemplate retryTemplate = new RetryTemplate();
    	/**
    	 *设置重试规则,即在什么情况下进行重试
    	 *什么情况下停止重试。源码中这部分存在
    	 *一个判断,判断的根据就是在zuul工程
    	 *的propertris中配置的zuul.retryable
    	 *该参数内容为true才可以具有重试功能。
    	 */
    	retryTemplate.setRetryPolicy(xxx);
    	/**
    	 *发起请求
    	 */
    	return retryTemplate.execute(callback);
    }

    到此为止我们不仅知道了zuul路由一次请求的整体过程,也明确了zuul因后端超时而触发重试的原理。可是似乎还存在着一个问题,就是超时问题。前面说过zuul把路由请求这个过程包装成一个HystrixCommnd,而在我的propertries文件中并没有设置Hystrix的超时时间(默认时间为1s),而read的超时时间是5s(前面源码部分介绍过)。这里就会有人问,因为最外层是采用Hystrix,而Hystrix此时已经超时了,为什么还允许它内部继续使用spring-retry进行重试呢?带着这个问题我查看了官方GitHub上的issues,发现有人对此问题提出过疑问。作者给出的回复是Hystrix超时的时候并不会打断内部重试的操作。 

    展开全文
  • spring-cloud-zuul 重试

    2021-02-13 20:15:44
    zuul支持路由到客户端进行重试,通过spring-retry 实现, 系统默认是不允许重试的,如果开启重试客户端则需要支持请求的幂等性 Zuul 服务 maven依赖 <dependency> <groupId>org.springframework....

       本文建立在spring-cloud-zuul环境搭建的基础上进行扩展介绍。

    介绍

       zuul支持路由到客户端进行重试,通过spring-retry 实现, 系统默认是不允许重试的,如果开启重试客户端则需要支持请求的幂等性

    Zuul 服务

    maven依赖

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.retry</groupId>
                <artifactId>spring-retry</artifactId>
            </dependency>

    配置

    spring:
      application:
        name: zuul
      cloud:
        loadbalancer:
          retry:
            enabled: true  #负载均衡启动重试
    
    zuul:
      routes:
        server:
          path: /server/**
          serviceId: server
      retryable: true  #是否开启重试功能
    
    hystrix:
      command:
        default:
          execution:
            isolation:
              thread:
                timeoutInMilliseconds: 6000
    
    ribbon:
      ConnectTimeout: 1000
      ReadTimeout: 1000
      MaxAutoRetries: 2   #对当前服务的重试次数
      MaxAutoRetriesNextServer: 1  #切换相同Server的次数
      OkToRetryOnAllOperations: true # 对所有请求都重试

     

    展开全文
  • 详解Spring Cloud Zuul重试机制探秘简介本文章对应spring cloud的版本为(Dalston.SR4),具体内容如下:开启Zuul功能通过源码了解Zuul的一次转发怎么开启zuul的重试机制Edgware.RC1版本的优化开启Zuul的功能首先如何...
  • 使用spring cloud zuul的时候,配置重试机制的时候要注意:重试配置依赖spring的retry,如果不引入此jar包,配置将无效&lt;dependency&gt; &lt;groupId&gt;org.springframework.retry&lt;/...
  • zuul 重试 配置与实践

    2018-11-17 16:37:47
    重试 必要步骤 1.必须要引入maven依赖,否则不生效 &lt;dependency&gt; &lt;groupId&gt;org.springframework.retry&lt;/groupId&gt; &lt;artifactId&gt;spring-retry&lt;/...
  • 使用spring cloud[Dalston.SR1]版本,如果加了如下重试参数还不生效: # hystrix的超时时间必须大于ribbon的超时时间 hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000 # 开启...
  • 我在配置了 Zuul的 熔断和重试以后 ,一直到ribbo超时后就直接返回错误编码 hystrix不生效,经过板砖发现 需要在zuul中加入 <groupId>org.springframework.retry <artifactId>spring-retry <version>...
  • 在使用E版本的Spring Cloud Netflix Zuul内置的Ribbon重试功能时,发现Ribbon有一个非常有用的特性: 如果某个服务的某个实例经常需要重试,Ribbon则会在自己维护的一个缓存(serverStatsCache)里将其临时标记为...
  • 问题: 在zuul中想要开启客户端负载均衡的重试机制,网上有很多文章介绍,但是我尝试一下他们提供的配置,发现不起作用。...这是zuul重试机制必须依赖的一个包,但很多文章没有提及。 <dependency> ..
  • Zuul ribbon 重试失效分析

    千次阅读 2019-06-05 21:12:59
    Zuul ribbon 重试机制 问题描述: Zuul转发POST请求接口异常,read timeout,没有进行重试,期望进行重试! 配置参数模拟 spring.cloud.loadbalancer.retry.enabled=true ribbon.ConnectTimeout=5000 ribbon....
  • 主要介绍了Spring Cloud Zuul重试配置详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 项目介绍: 两个service,一个client,一个gateway client通过feign调用service,在...部署zuul作为gateway并设置和client中一样的ribbon的超时和重试参数以及hystrix的熔断时间,通过gateway调用client,发生重...
  • Spring Cloud Zuul模块本身就包含了对于hystrix和ribbon的依赖,当我们使用zuul通过path和serviceId的组合来配置路由的时候,可以通过hystrix和ribbon的配置调整路由请求的各种时间超时机制。1 ribbon配置举例配置...
  • SpringCloud学习03之api服务网关zuul反向代理及重试配置 springCloud学习04之api服务网关zuul回退fallback 注意:重试的开启需要处理幂等和部分接口响应超时重试带来的流量异常。理论上GET方法可以开启重试,...
  • zuul 并发下的重试配置

    千次阅读 2017-12-18 10:29:48
    zuul 并发下的重试配置 zuul 请求重试
  • Spring Cloud Zuul模块本身就包含了对于hystrix和ribbon的依赖,当我们使用zuul通过path和serviceId的组合来配置路由的时候,可以通过hystrix和ribbon的配置调整路由请求的各种时间超时机制。 1 ribbon配置举例配置...
  • 文章目录概要依赖导入...当出现Spring Retry时,负载平衡的Zuul会自动重试任何失败的请求(如下示例配置,如果后端服务关闭,Zuul重试2次)。 Zuul使用的默认HTTP客户端现在由Apache HTTP客户端而不是已弃用的Ri
  • Ribbon、Feign、Hystrix和Zuul超时重试设置(二)
  • Zuul超时问题,微服务响应超时,zuul进行熔断 Spring Cloud各组件超时总结 Zuul网关Ribbon重试 SpringCloud重试机制配置 zuul 重试设置
  • Spring Cloud之网关Zuul开启重试

    千次阅读 2018-07-03 20:12:17
    在Spring Cloud体系中,如果网关要支持重试要配置的点蛮多的,等我一一道来:网关工程pom.xml中要加入org.springframework.retry依赖&lt;dependencies&gt; &lt;dependency&gt; &lt;groupId...
  • zuul网关自动重试配置

    2021-04-14 15:56:03
    zuul: retryable: true host: #最大连接数量 max-per-route-connections: 2000 socket-timeout-millis: 30000 connect-timeout-millis: 3000 #ribbon的超时时间 ribbon: ReadTimeout: 60000 ConnectTimeout:...
  • zuul 熔断后重试

    2019-12-13 18:10:00
    application.yml 开启重试 zuul: routes: vehicle-admin: path: /admin/** sensitiveHeaders: Cookie,Set-Cookie stripPrefix: false vehicle-detect: path: /detect/** sensitiveHeaders: Cookie,Set-...
  • 服务网关zuul服务重试配置(学习笔记2020.03.13) 前言: 集成网关服务,内部所有的其他微服务的调用,都将通过网关路由转发过去,不对外直接暴露微服,对外只暴露网关服务。而且一般内部服务会部署多个实例,zuul内部...
  • zuul重连配置

    2019-10-24 16:17:00
    #该参数用来开启重试机制 spring.cloud.loadbalancer.retry.enabled=true #断路器的超时时间,断路器的超时时间需要大于ribbon的超时时间,不然不会触发重试。 hystrix.command.default.execution.isolation....

空空如也

空空如也

1 2 3 4 5 ... 14
收藏数 269
精华内容 107
关键字:

zuul重试