希望长大对我而言,是可以做更多想做的事,而不是被迫做更多不想做的事...... 首页 hystrix工作原理及高级特性 丁D 学无止境 2019-08-08 15:42 44607已阅读 hystrix 摘要本文将讲解Hystrix的工作原理,并从中分析其高级特性。 上一篇我们已经介绍了hystrix及其用法,现在我们就继续分析Hystrix,和分析Hystrix的一些高级特性,**请求缓存**,**降级**。 ###一、执行流程原理(8步) **1.构建一个HystrixCommand或者HystrixObservableCommand** HystrixCommand:返回一个数据 HystrixObservableCommand:返回多条数据 编写一个class继承HystrixCommand或HystrixObservableCommand,实现方法run/construct方法, 在构造函数中传入参数,并设置线程池 **2.执行Command** 要执行Command,必须new一个Command并调用 execute(),queue(),observe(),toObservable() 4个方法 **execute**:同步,直接阻塞直到依赖服务返回 **queue**:异步,返回一个Future,属于异步调用,后面可以通过Future获取单条结果 **observe**:调用的时候会立即执行 run/construct **toObservable**:调用的时候不会立即执行 run/construct,,要等订阅的时候才会执行。有延迟执行的特性 execute queue 只适用于 HystrixCommand **3.检查是否开启缓存** 检查请求缓存request cache 如果存在数据直接返回 **4.检查短路器是否开启** 如果开启直接去调用降级方法 **5.检查线程池/队列/semaphore是否已经满了** semaphore没有队列 线程池有等待队列 如果满了直接调用 降级方法 **6.具体执行command** 执行失败异常,调用降级方法 执行超时,调用降级方法(信号量没有超时) 超时了不会kill执行run/construct的线程(所以后面可能执行成功) **7.短路健康检查** 对于一个依赖服务的调用不管是成功还是失败,拒绝,超时,都会通知断路器,断路器进行统计成功/失败的比例,从而判断是否打开短路器 **8.调用fallback** 在以下几种情况中,hystrix会调用fallback降级机制:run()或construct()抛出一个异常,短路器打开,线程池/队列/semaphore满了,command执行超时了 一般在降级机制中,都建议给出一些默认的返回值,比如静态的一些代码逻辑,或者从内存中的缓存中提取一些数据,尽量在这里不要再进行网络请求了 ![alt](/upload/微信图片_20190814094439.png ) 不同的command执行方式,其fallback为空或者异常时的返回结果不同 对于execute(),直接抛出异常 对于queue(),返回一个Future,调用get()时抛出异常 对于observe(),返回一个Observable对象,但是调用subscribe()方法订阅它时,理解抛出调用者的onError方法 对于toObservable(),返回一个Observable对象,但是调用subscribe()方法订阅它时,理解抛出调用者的onError方法 ###二、高级特性 **请求缓存** 查看是否有请求缓存,如果有,直接拿缓存返回 首先,我们需要一个概念,**request context 请求上下文**,在tomcat中,每一个请求都是一个请求上下文。所以我们通常在filter中,先初始化一个**request context** 在一个请求上下文中,我们可能会需要调用不同的依赖服务,有的依赖服务还会重复调用多次。在一次请求上下文中了,如果我们多次调用同一个command,调用同一个接口,参数都一样,那么我们可以认为,返回的结果也一样,这样就可以取第一次调用的结果返回,用在一次请求上下文中反复多次的执行一样的command,提升整个请求的性能 HystrixCommand和HystrixObservableCommand都可以指定一个缓存key,然后hystrix会自动进行缓存,接着在同一个request context内,再次访问的时候,就会直接取用缓存 用请求缓存,可以避免重复执行网络请求 多次调用一个command,那么只会执行一次,后面都是直接取缓存 对于请求缓存(request caching),请求合并(request collapsing),请求日志(request log),等等技术,都必须自己管理HystrixReuqestContext的声明周期 在一个请求执行之前,都必须先初始化一个request context HystrixRequestContext context = HystrixRequestContext.initializeContext(); 然后在请求结束之后,需要关闭request context context.shutdown(); 一般来说,在java web来的应用中,都是通过filter过滤器来实现的 ```js public class HystrixRequestContextServletFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try { chain.doFilter(request, response); } finally { context.shutdown(); } } } @Bean public FilterRegistrationBean indexFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(new IndexFilter()); registration.addUrlPatterns("/"); return registration; } ``` **fallback降级** 当我们调用依赖服务,当调用依赖服务报错,超时,线程池/信号量,如果资源池已满,或者断路器打开了,这个时候需要去调用fallback方法进行降级 **两种最经典的降级机制:纯内存数据,默认值** 纯内存数据 : fallback降级就从本地内存中获取一份过期的数据,先凑合着用着 默认值: 设定一个默认值 **设置超时** 在分布式系统中,我们需要调用各种各样的依赖服务,然而,依赖服务的开发者是谁我们不知道,并且水平如何我们也不知道,所以可能会导致依赖的接口性能不是很稳定,可能出现各种超时,导致线程资源hang住,吞吐量大幅度下降,甚至服务崩溃。 大量接口调用很慢,大量线程就会卡死,资源隔离,线程池的线程就卡死,所以需要超时控制。 (1)控制是否大开timeout机制,默认true ```js //execution.timeout.enabled HystrixCommandProperties.Setter().withExecutionTimeoutEnabled(boolean value) ``` (2)手动设置超时时间默认1000毫秒 ```js //execution.isolation.thread.timeoutInMilliseconds HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(int value) ``` ###三、短路器的工作原理 1、短路器打开的前提是经过短路器的流量超过了一定的阈值。比如说在10s内,经过短路器的流量必须达到20个才会判断要不要短路。 2、到达短路器的流量异常占比必须超过一定的阈值。比如我们说在10s,经过短路器的流量达到了30个,同事其中异常的访问数量,占到了一定的比例,比如说50%的请求都是异常的,那就开启短路。 3、短路器达到了一定的阈值,且异常占比也达到了阈值,那么此时短路器就会从close状态转换到open状态。 4、短路器打开的时候,所有经过该短路器的请求全部被短路,不调用后端服务,直接fallback降级。 5、经过一段时间之后,短路器会处于半开状态,让一条请求经过短路器,看能否正常调用,如果调用成功,那么短路器就会自动关闭。 ###四、短路器相关配置 1、circuitBreaker.enabled:控制短路器是否允许工作,包括跟踪依赖服务调用的健康状况,以及对异常情况过多时是否允许触发短路,默认是true。HystrixCommandProperties.Setter().withCircuitBreakerEnabled(boolean value) 2、circuitBreaker.requestVolumeThreshold:设置一个rolling window,滑动窗口中,最少要有多少个请求时才触发开启短路。(如果设置为20(默认值),那么在一个10秒的滑动窗口内,如果只有19个请求,即使这19个请求都是异常的,也是不会触发开启短路器的。)HystrixCommandProperties.Setter().withCircuitBreakerRequestVolumeThreshold(int value) 3、circuitBreaker.sleepWindowInMilliseconds:设置在短路之后,需要在多长时间内直接reject请求,然后在这段时间之后,再重新到holf-open状态,尝试允许请求通过以及自动恢复,默认值是5000毫秒。HystrixCommandProperties.Setter().withCircuitBreakerSleepWindowInMilliseconds(int value) 4、circuitBreaker.errorThresholdPercentage:设置异常请求量的百分比,当异常请求达到这个百分比时,就触发打开短路器,默认是50,也就是50%。HystrixCommandProperties.Setter().withCircuitBreakerErrorThresholdPercentage(int value) 5、circuitBreaker.forceOpen:如果设置为true的话,直接强迫打开短路器,相当于是手动短路了,手动降级,默认false。HystrixCommandProperties.Setter().withCircuitBreakerForceOpen(boolean value) 6、circuitBreaker.forceClosed:如果设置为ture的话,直接强迫关闭短路器,相当于是手动停止短路了,手动升级,默认false。HystrixCommandProperties.Setter().withCircuitBreakerForceClosed(boolean value) ``` /** * 获取商品信息 */ public class GetProductInfoCommand extends HystrixCommand { private Long productId; public GetProductInfoCommand(Long productId) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ProductInfoService")) .andCommandKey(HystrixCommandKey.Factory.asKey("GetProductInfoCommand")) .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("GetProductInfoPool")) .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter() .withCoreSize(15) .withQueueSizeRejectionThreshold(10)) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() .withCircuitBreakerRequestVolumeThreshold(30)//10秒内有30个请求时,触发断路器 .withCircuitBreakerErrorThresholdPercentage(40)//当异常达到百分之40时,触发断路器 .withCircuitBreakerSleepWindowInMilliseconds(3000))//在3秒内直接refect请求走降级,3秒过后进入半开状态 ); this.productId = productId; } @Override protected ProductInfo run() throws Exception { System.out.println("调用接口,查询商品数据,productId=" + productId); if(productId.equals(-1L)) { throw new Exception(); } return JSONObject.parseObject("数据", ProductInfo.class); } @Override protected ProductInfo getFallback() { ProductInfo productInfo = new ProductInfo(); productInfo.setName("降级商品"); return productInfo; } } ``` 很赞哦! (1) 上一篇:hystrix基础 下一篇:分布式系统的接口幂等性设计 目录 点击排行 Elasticsearch6.3.2之x-pack redis哨兵 2019-07-09 22:05 Redis+Twemproxy+HAProxy+Keepalived 2019-07-12 17:20 GC优化策略和相关实践案例 2019-10-10 10:54 JVM垃圾回收器 2019-10-10 10:23 标签云 Java Spring MVC Mybatis Ansible Elasticsearch Redis Hive Docker Kubernetes RocketMQ Jenkins Nginx 友情链接 郑晓博客 佛布朗斯基 凉风有信 MarkHoo's Blog 冰洛博客 南实博客 Rui | 丁D Java研发工程师 生活可以用「没办法」三个字概括。但别人的没办法是「腿长,没办法」、「长得好看,没办法」、「有才华,没办法」。而你的没办法,是真的没办法。 请作者喝咖啡