网关相关配置
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才会生效。
模拟场景
-
调用关系
- 代码说明
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
-
此时业务日志显示正常处理,时间大概为2秒
结论
我们发现现在这种情况并没有进行重试,因为我们可以看到业务日志只被调用了一次,咦!! 等等,还记得我们上面提到的MaxAutoRetries属性和MaxAutoRetriesNextServer吗,是不是因为这个所以不对当前机器进行重试,如果在不改变此值的前提下,我们再启动一台同等服务呢?
两台服务
- 结论
只有一台服务被调用,并没有重试到另一台服务上
单台服务 MaxAutoRetries修改为大于0
当MaxAutoRetries大于0时,调用也没有发生变化。
至此说明实验一中重试未起作用。
实验二 (使用okhttp)
两台服务(默认配置)
突然有一个想法,网关重试机制会不会和ribbon底层使用的http有关呢,默认情况下ribbon使用的是httpclient,那么我如果换成使用okhttp呢,于是,我把ribbon底层换成了okhttp又重复了一遍实验一
-
网关依旧超时
但此时可以看出超时时间大致为4秒
-
服务被调用了两次
神奇的情况发生了,两台服务都被调用了,貌似自动开启了重试功能
我们通过日志可以看出业务处理时间分别为
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秒超时呢?
按照我们之前的计算公式应该是8秒才对啊ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1) ribbonTimeout =(1000+100)*(1+1)*(1+1)=8000(ms)
通过打印异常堆栈,可以发现其中的端倪
异常已经清晰的告诉我们了原因:
Number of retries on next server exceeded max 1 retries ,就是重试机器的次数已经答到了设置的上限,因为我们MaxAutoRetriesNextServer设置的是1,意思就是我们重试一台。因此我们吧
单台服务 MaxAutoRetries修改为2
ribbon.MaxAutoRetries: 2
-
网关超时时间已经变为了大概6秒
-
业务被调用了6次 也是一秒一次
- 结论
ribbon超时时间虽然是8秒,但实际过程中还要看重试策略。
实验三
zuul使用httpClient并支持重试
- 修改pom 增加重试包
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
打开重试开关
zuul.retryable=true
-
网关发起调用
-
服务调用日志
实验总结
当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,
httclient是通过RibbonRoutingFilter.setResponse(ClientHttpResponse resp)
-
在AbstractRibbonCommand中我们可以看到两个计算时间的方式
getRibbonTimeout 上面我们已经说过,除些之外还有一个计算hystrix的时间方法getHystrixTimeout,默认情况下hystrix超时时间defaultHystrixTimeout为0,网上大部分都说默认是1秒,但其实我认为在zuul网关这个场景下,这种说法不对的。如果我们设置了hystrix超时时间,刚会已我们设置的为准,但如果我们不设置,代码会使用ribbon的超时时间为hystrix超时时间。
而且当hystrixTimeout设置的值小于ribbonTimeout,则会打印警告。也就是说当hystrixTimeout<ribbonTimeout ribbon的超时设置就没有意义了,因为提前触发了hystrix的服务降级策略.但是反过来,如果 hystrixTimeout>ribbonTimeout ribbonTimeout的设置也没意义了啊,因为ribbon超时了,也触发网关回退机制了,hystrixTimeout就没意思了,但感觉这点设计的比较乱。
此外,重试默认都是只支持get请求,如果我把请求方式修改为post重试是不生效的,我们需要设置OkToRetryOnAllOperations为true, 这种情况不太建议,因为post请求大多都是写入请求,如果要支持重试,服务自身的幂等性一定要健壮。
zuul网关重试就先写到这,主要是记录了一下我自己的使用当中遇到的情况。欢迎拍砖。