美文网首页
zuul网关重试机制探索

zuul网关重试机制探索

作者: 千里浪打浪 | 来源:发表于2019-01-16 17:23 被阅读0次

    网关相关配置

    1.zuul相关的默认配置 springcloud(F版)

    属性 默认值 描述
    zuul.retryable false 是否开启重试
    ribbon.ConnectTimeout 1000 链接超时时间
    ribbon.ReadTimeout 1000 读超时时间
    ribbon.MaxAutoRetries 0 对第一次请求的服务的重试次数
    ribbon.MaxAutoRetriesNextServer 1 要重试的下一个服务的最大数量(不包括第一个服务)
    ribbon.OkToRetryOnAllOperations false 所有请求都重试
    ribbon.MaxTotalHttpConnections 200 ?
    ribbon.MaxConnectionsPerHost 50 ?
    hystrix.command.default.execution.timeout.enabled true 是否开启

    有些书上说,配置当中的ConnectTimeout和ReadTimeout是当HTTP客户使用HttpClient的时候生效的,参数会被设置到HttpClient中,但我在使用过程中,并不是只有HttpClient才会生效。

    ribbon默认配置 ribbon默认配置

    模拟场景

    1. 调用关系


      模拟调用关系
    2. 代码说明
      zuul主要是负责把请求转发到后面的服务上,为了模拟业务处理场景,服务暴露了一个sleep借口,接口作用是根据传过来的参数模拟业务处理时间。
      @RequestMapping(value = "/testSleep",method = 
      {RequestMethod.GET,RequestMethod.POST} )
      public ApiResult testSleep(@RequestParam("sleep") int sleep){
          logger.info("sleep..........start");
         try{
             Thread.sleep(sleep*1000);
         }
         catch (Exception e){
             logger.error(e.toString());
         }
          logger.info("sleep..........end");
          return ApiResult.SUCCESS();
      }
      
      

    实验

    实验一 裸奔配置(zuul全部走默认配置)

    单台服务

    默认情况下ribbon超时时间为1秒,我们模拟一个2秒的业务让ribbon超时。

    • 此时客户端返回了错误结果,此处进行了zuul回退机制处理


      客户端模拟
    • 业务处理2秒时,网关报了异常,日志中的ProducerFallback是因为在zuul网关中加了Fallback回退措施,打印出的异常为Read timed out


      zuul超时
    • 此时业务日志显示正常处理,时间大概为2秒


      业务调用一次
    • 结论
      我们发现现在这种情况并没有进行重试,因为我们可以看到业务日志只被调用了一次,咦!! 等等,还记得我们上面提到的MaxAutoRetries属性和MaxAutoRetriesNextServer吗,是不是因为这个所以不对当前机器进行重试,如果在不改变此值的前提下,我们再启动一台同等服务呢?

    两台服务

    网关超时 第一台服务命中 第二台服务未命中
    • 结论
      只有一台服务被调用,并没有重试到另一台服务上

    单台服务 MaxAutoRetries修改为大于0

    当MaxAutoRetries大于0时,调用也没有发生变化。
    至此说明实验一中重试未起作用。

    实验二 (使用okhttp)

    两台服务(默认配置)

    突然有一个想法,网关重试机制会不会和ribbon底层使用的http有关呢,默认情况下ribbon使用的是httpclient,那么我如果换成使用okhttp呢,于是,我把ribbon底层换成了okhttp又重复了一遍实验一


    开启okhttp
    • 网关依旧超时
      但此时可以看出超时时间大致为4秒


      zuul超时3.png
    • 服务被调用了两次
      神奇的情况发生了,两台服务都被调用了,貌似自动开启了重试功能
      我们通过日志可以看出业务处理时间分别为
      18:18:11到18:18:13 处理时间2秒
      18:18:12到18:18:14 处理时间2秒
      在服务处理1秒后进行了重试


      第一台服务
      第二台服务
    • 为什么重试时网关会是4秒超时呢?
      还记我们之前的截图吗,ribbon的超时时间是通过公式计算出来的

      ribbonReadTimeout :1
      ribbonConnectTimeout:1
      maxAutoRetries:0
      maxAutoRetriesNextServer :1
      
      ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) 
                      * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1)
      
      ribbonTimeout = (1000+1000)*(0+1)*(1+1)=4000(ms)
      
      
    • 结论
      服务打到其中一台服务器上,因为ribbon.ReadTimeout为1秒,此时业务处理时间需要2秒,所以1秒后,也就是18:18:12 的时候进行了重试,
      ribbon.MaxAutoRetries为0,所以没有在本服务进行重试,而是选择了另外一台服务。

    单台服务 MaxAutoRetries修改为1

    ribbon.MaxAutoRetries: 1

    • 网关超时
      但是我们细心一点可以发现,此时的网关也是4秒后超时的


      网关超时
    • 服务调用
      日志显示业务被触发了4次。1秒一次


      服务调用日志
    • 为什么服务被调用了4次
      现象应该是

      4次重试顺序.png
    • 为什么重试时网关也是4秒超时呢?
      按照我们之前的计算公式应该是8秒才对啊

       ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) 
              * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1)
       ribbonTimeout =(1000+100)*(1+1)*(1+1)=8000(ms)
      
      

    通过打印异常堆栈,可以发现其中的端倪

    异常.png

    异常已经清晰的告诉我们了原因:
    Number of retries on next server exceeded max 1 retries ,就是重试机器的次数已经答到了设置的上限,因为我们MaxAutoRetriesNextServer设置的是1,意思就是我们重试一台。因此我们吧

    单台服务 MaxAutoRetries修改为2

    ribbon.MaxAutoRetries: 2

    • 网关超时时间已经变为了大概6秒


      网关6秒.png
    • 业务被调用了6次 也是一秒一次


      6次调用.png
    • 结论
      ribbon超时时间虽然是8秒,但实际过程中还要看重试策略。

    实验三

    zuul使用httpClient并支持重试

    • 修改pom 增加重试包
          <dependency>
                <groupId>org.springframework.retry</groupId>
                <artifactId>spring-retry</artifactId>
            </dependency>
    
    • 打开重试开关
      zuul.retryable=true

    • 网关发起调用


      网关日志
    • 服务调用日志


      1机器被调用
      另一台机器被调用

    实验总结

    • 当ribbon使用的是httpclient时,重试机制是默认关闭的,如果要启动重试机制需要在项目中引用spring-retry包,以及手工打开zuul.retryable=true设置,其实除此之外spring.cloud.loadbalancer.retry.enabled=true 也是需要设置的只不过这个值默认为true,所以此处可以忽略设置。

    • 当ribbon使用的是okhttp时,重试机制是自动打开的,重试的效果与我们设置的ribbon超时时间以及重试次数都有关系。

    • 虽然二种机制都能达到zuul网关调用的重试,但细心一点的朋友还是应该可以看出一点区别的,使用httpclient进行重试时,客户端进行重试超时返回时,控制台并没有打印Exception日志,但是当我们替换成okhttp后,控制台会打印出Exception日志 如下图对比


      日志对比

      okhttp是通过AbstractRibbonCommand.getFallbackResponse,


      AbstractRibbonCommand
      httclient是通过RibbonRoutingFilter.setResponse(ClientHttpResponse resp)
      RibbonRoutingFilter
    • 在AbstractRibbonCommand中我们可以看到两个计算时间的方式
      getRibbonTimeout 上面我们已经说过,除些之外还有一个计算hystrix的时间方法getHystrixTimeout,默认情况下hystrix超时时间defaultHystrixTimeout为0,网上大部分都说默认是1秒,但其实我认为在zuul网关这个场景下,这种说法不对的。如果我们设置了hystrix超时时间,刚会已我们设置的为准,但如果我们不设置,代码会使用ribbon的超时时间为hystrix超时时间。


      hystrixTimeout计算

      而且当hystrixTimeout设置的值小于ribbonTimeout,则会打印警告。也就是说当hystrixTimeout<ribbonTimeout ribbon的超时设置就没有意义了,因为提前触发了hystrix的服务降级策略.但是反过来,如果 hystrixTimeout>ribbonTimeout ribbonTimeout的设置也没意义了啊,因为ribbon超时了,也触发网关回退机制了,hystrixTimeout就没意思了,但感觉这点设计的比较乱。

    • 此外,重试默认都是只支持get请求,如果我把请求方式修改为post重试是不生效的,我们需要设置OkToRetryOnAllOperations为true, 这种情况不太建议,因为post请求大多都是写入请求,如果要支持重试,服务自身的幂等性一定要健壮。

    zuul网关重试就先写到这,主要是记录了一下我自己的使用当中遇到的情况。欢迎拍砖。

    相关文章

      网友评论

          本文标题:zuul网关重试机制探索

          本文链接:https://www.haomeiwen.com/subject/qdmedqtx.html