由于测试 Feign + Hystrix 搭配模式下的降级(fallback)超时时间自定义问题,算是踩了个坑,而后就顺便查+测试了下 Zuul、Ribbon + Hystrix 模式下分别怎么设置html
测试这些东西费了很多力气,由于这几个模块要么搭配使用、要么有内部依赖别的模块、要么对其余模块作了封装,这个配置项就变得千奇百怪,并且网上的东西,一直以为有个很"严重"的问题,就是版本不明,版本号都不同,解决方案或者说配置方式可能彻底不一样,而不少的文章中也没有说起他们用的是哪一个版本,搞得我是晕头转向(毕竟我不是这些服务模块的开发者或者长期的使用者,不是很是了解这些东西的版本演进过程)java
因此这里是查了很多的资料,测试经过了一些方案,也算是本身总结记录一下git
注意!github
这里都是基于有 Eureka 作服务中心为前提的spring
这个栗子的源码看这里app
最基本的配置,是 Hystrix 本身的一长串配置:hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
,但在 Feign 模块中,单独设置这个超时时间不行,还要额外设置 Ribbon 的超时时间,好比:socket
hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 5000 ribbon: ReadTimeout: 5000 ConnectTimeout: 5000
关于 Hystrix 的配置,这里有官方的说明:ide
Default Value | 1000 |
Default Property | hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds |
Instance Property | hystrix.command.HystrixCommandKey.execution.isolation.thread.timeoutInMilliseconds |
How to Set Instance Default | HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(int value) |
能够看到实例配置中,替代 default 的,是 HystrixCommandKey,这个值在下面会说到工具
若是更进一步,想把超时时间细分到不一样的 service 实例上也能够实现,好比:学习
@FeignClient( value = "hello-service", fallback = MyFeignClientHystric.class) public interface MyFeignClient { @RequestMapping("/hello") String sayHelloByFeign(); @RequestMapping("/why") String sayWhyByFeign(); }
hystrix: command: "MyFeignClient#sayWhyByFeign()": execution: isolation: thread: timeoutInMilliseconds: 9000 default: execution: isolation: thread: timeoutInMilliseconds: 2000 ribbon: ReadTimeout: 5000 ConnectTimeout: 5000
这种写法是把默认的超时时间改为2秒,而把另一个自定义的 Feign 客户端中的某方法超时时间定成9秒(格式是类名#方法名()
,若是方法有入参,也要把入参的类型拼上),这里的 MyFeignClient#sayWhyByFeign()
就表明了上面说到的 commandKey
,而这种写法,则是 Feign 模块中特殊的:
ryanjbaxter commented on 26 JulIf you had a Feign client called MyClient and it had a method called search that took in a single String parameter than you would use the following property
hystrix.command.MyClient#search(String).execution.isolation.thread.timeoutInMilliseconds
看 issue 里 Spring Cloud 的官方人员的说法,这种格式是他们进行的封装,因此咱们要设置,就只能这么写
这个栗子的源码看这里
在使用 Ribbon 时,只须要配置 Hystrix 的超时时间就能够生效,不须要额外配置 Ribbon 的超时时间,好比:
hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 9000
想细分服务超时时间时:
若是是同一个服务实例下的不一样接口,想使用不一样的超时时间,能够把 @HystrixCommand
中的 commandKey
定义成不一样的值,而后在 yml 中分别设置
@HystrixCommand( commandKey = "helloService-sayHello", fallbackMethod = "sayHelloDefault") public String sayHelloByRibbon() { return restTemplate.getForObject("http://HELLO-SERVICE/hello", String.class); } public String sayHelloDefault() { return "hello service error, this is default say hello method"; } @HystrixCommand( commandKey = "helloService-sayWhy", fallbackMethod = "sayWhyDefault") public String sayWhyByRibbon() { return restTemplate.getForObject("http://HELLO-SERVICE/why", String.class); } public String sayWhyDefault() { return "hello service error, this is default say why method"; }
hystrix: command: helloService-sayWhy: execution: isolation: thread: timeoutInMilliseconds: 5000 default: execution: isolation: thread: timeoutInMilliseconds: 1500
若是想统一设置同一个服务实例中各方法的超时时间,经测试,能够把不一样方法上的 commandKey
设置成相同的值,这样在 yml 中对该 key 作超时配置就能同时生效了:
@HystrixCommand( commandKey = "helloService", fallbackMethod = "sayHelloDefault") public String sayHelloByRibbon() { return restTemplate.getForObject("http://HELLO-SERVICE/hello", String.class); } public String sayHelloDefault() { return "hello service error, this is default say hello method"; } @HystrixCommand( commandKey = "helloService", fallbackMethod = "sayWhyDefault") public String sayWhyByRibbon() { return restTemplate.getForObject("http://HELLO-SERVICE/why", String.class); } public String sayWhyDefault() { return "hello service error, this is default say why method"; }
hystrix: command: helloService: execution: isolation: thread: timeoutInMilliseconds: 5000 default: execution: isolation: thread: timeoutInMilliseconds: 1000
这个栗子的源码看这里,Zuul 中的降级是用了 FallbackProvider
,简单的使用能够看我源码中的 HelloFallbackProvider.java 和 HiFallbackProvider.java,我也是参考了官方的文档说明和例子
zuul 中配置超时时间,据官方的介绍,分两种状况:
ribbon.ReadTimeout
和 ribbon.SocketTimeout
设置zuul.host.connect-timeout-millis
和 zuul.host.socket-timeout-millis
设置由于个人代码中是用 serviceId 的方式,因此参考了第一种配置,好比:
zuul: routes: helloService: path: /hello-service/** serviceId: hello-service hiService: path: /hi-service/** serviceId: hi-service ribbon: ConnectTimeout: 5000 ReadTimeout: 5000
Ribbon 的配置项还能够加一个 ClientName
为前缀(这个方法的出处在官方的 wiki),区分不一样客户端下的配置,这个 ClientName
我是直接用了 serviceId,测试了正常,但还能够用或者说应该用什么值,这个我尚未找到官方的说明。。
zuul: routes: helloService: path: /hello-service/** serviceId: hello-service hiService: path: /hi-service/** serviceId: hi-service hello-service: ribbon: ConnectTimeout: 5000 ReadTimeout: 5000 hi-service: ribbon: ConnectTimeout: 500 ReadTimeout: 500 hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 3000
另外还作了测试,若是同时配置了 Ribbon 和 Hystrix 的超时时间,则以最小的为准,好比上述的配置中,若是 hi-service 的接口调用超过了 0.5 秒,则就会触发超时
目前的学习和测试结果来看:
commandKey
来实现超时时间的配置commandKey
,咱们不能自定义,因此同一个 FeignClient 下的服务接口不能方便的统一配置,若是有相应的业务需求,或许只能对每一个特殊的接口方法作独立的超时配置(找到新方法的话再回来更新)