背景,网上看到很多关于hystrix的配置都是没生效的,如:
一.先看测试环境搭建:
order 服务通过feign 的方式调用了product 服务的getProductInfo 接口
//------------ order 服务的调用接口--------------- @FeignClient(name ="product",fallback =ProductHystrix.class) @Primary public interface ProductService { @RequestMapping("/info/{id}") Product getProductInfo(@PathVariable("id") Integer id); } @Component public class ProductHystrix implements ProductService{ @Override public Product getProductInfo(Integer id) { System.out.println("被熔断;了"); Product product = new Product(); product.setName("熔断了。。。。。"); return product; } } // product 服务提供的接口------------------ @Controller public class ProductController { @RequestMapping("/info/{id}") @ResponseBody @MyLogAnnotation public Product getProductInfo(@PathVariable("id") Integer id){ Product product = new Product(); product.setId(id); product.setName("苹果手机"); return product; } }
order 服务的application.yml 开启feign:hystrix:enabled: true
二. Hystrix 的超时时间怎么设置:
直接上代码:hystrix 任何相关配置都可以在下面的配置类配置,我这里修改了核心线程数和最大队列数已经超时时间
package com.yang.xiao.hui.order.controller; import com.netflix.hystrix.*; import feign.Feign; import feign.Target; import feign.hystrix.HystrixFeign; import feign.hystrix.SetterFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; import java.lang.reflect.Method; @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class }) public class FeignConfig { @Bean @Scope("prototype") @ConditionalOnProperty(name = "feign.hystrix.enabled") public Feign.Builder feignHystrixBuilder() { HystrixFeign.Builder builder = HystrixFeign.builder(); SetterFactory setterFactory= new SetterFactory(){ @Override public HystrixCommand.Setter create(Target<?> target, Method method) { String groupKey = target.name(); String commandKey = Feign.configKey(target.type(), method); //HystrixThreadPoolProperties 线程池相关配置 HystrixThreadPoolProperties.Setter setter = HystrixThreadPoolProperties.Setter().withCoreSize(100).withMaxQueueSize(200); //HystrixCommandProperties 熔断器相关属性配置 HystrixCommandProperties.Setter setter1 = HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(6000); return HystrixCommand.Setter .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey)) .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey)) .andThreadPoolPropertiesDefaults(setter) .andCommandPropertiesDefaults(setter1); } }; builder.setterFactory(setterFactory); return builder; } }
启动服务:通过浏览器输入http://localhost:8674/info/3 调用order 服务
跟进去看HystrixCommand的创建:
由此可见线程池和熔断超时时间都已经改变了,证明我们的配置生效了
原理分析:
在FeignClientsConfiguration这个配置类中有一段代码:
然后跟进看到默认的对象:
HystrixCommandProperties对象有个默认的超时时间,默认是1s:
那么,我们根据上面的分析,当容器中存在HystrixFeign.builder就不会再创建该bean 了,所以我们可以自己创建一个HystrixFeign.builder 然后调用setterFactory(SetterFactory setterFactory)来修改默认的配置,也就是上面我们自己定义的配置类
熔断器的熔断时间是从调用下面的方法开始计算的:
三.feign超时时间怎么设置?
我们继续跟进刚才的调用方法:
在这里默认连接时间是10s,读取时间是60s,那么这个类是怎么创建的呢?
从上面可以知道,Options对象默认已经有个时间配置了,然而我们继续跟踪代码:
我们看这里:
Options对象经过getClientConfig(options, clientName) 方法,就从10s的连接时间变成了1s,60s的读取时间也变成了1s,上面源码分析可以知道:
如果我们配置了feign的超时时间,那么就会以我们配置的时间为准,如果没有配置,那么就取ribbon的超时时间,2者只能有一个生效,而ribbon默认超时时间是1秒
继续跟进:
上述代码可见,feign或者ribbon所配置的超时时间,最终都是在HttpUrlConnection中生效
那么,我们如何修改feign的配置时间呢?
我们回到这里:
这里说了,如果容器没有该bean才会默认创建,那我们就自己创建一个注入到spring容器中:
在order 服务中:
@Configuration public class Config { @Bean public Request.Options feignRequestOptions() { Request.Options options = new Request.Options(2000, TimeUnit.MILLISECONDS, 2000, TimeUnit.MILLISECONDS, true); return options; } }
重启,重新调用:
看到配置已经生效了:
我们这里readTimeout 设置了2秒,如果我们在product服务睡眠3s看看:
product 服务没有睡眠时,正常情况调用结果如下:
product 服务代码修改:
order 服务再次调用:
可见,跟我们想象的一样
如果feign的超时时间设置为4s,而hystrix的熔断时间设置为2s看看:
再次调用:
重点是product服务的日志打印:
证明order 服务熔断了,但product 服务还是被调用了,说明feign的时间设置是没问题的
根据上面分析,Hystrix的熔断时间要大于Feign或Ribbon的connectTimeout+readTimeout
feign的超时时间如果要在application.yml中配置,改如何配置呢:
feign: hystrix: enabled: true client: config: default: #连接到目标的时间,此处会收到注册中心启动中的影响。设置为3秒钟,如果注册中心有明显的不在线,基本是毫秒级熔断拒绝 connectTimeout: 3000 #获取目标连接后执行的最长时间,设置为32秒,即服务最长时 readTimeout: 32000
为何能这样配置呢?
我们再次启动order服务,调用跟踪:
那ribbon的超时时间如何配置:
ribbon: ReadTimeout: 3000 ConnectTimeout: 3000
再次启动测试:
那么有人要问了,如果feign和ribbon同时配置,那么以谁的为准,前面已经分析过了,这里再次分析:
重新调用跟踪:
继续跟踪:
最后总结:
hystrix的熔断时间配置通过yml配置没法生效,可以通过配置类的方法来修改,feign的超时时间可以通过代码或者yml配置,ribbon的超时时间可以通过yml来配置
feign和ribbon的超时时间只能二选一,只要feign的超时时间配置了,就以feign的为准,hystrix的超时时间要大于feign/riboon的connectTimeout+readTimeout的和
最后一张图来总结: