Hystrix使用与分析

1、资源隔离

分布式环境中咱们不能确保调用的依赖服务不发生问题。诸如基础设施服务器宕机(数据库、缓存),外部依赖服务挂掉,网络抖动引发的链接超时。当这些状况发生时,大量请求迅速涌到链路上的故障节点,就会像繁忙的高速公路上发生交通事故短期内就会形成交通拥堵,直至瘫痪。html

不能让局部服务的故障不断扩散蔓延致使全局系统不可用,形成重大生产事故,因此要对外部依赖进行资源隔离,将故障控制在小范围内。java

hystrix两种隔离策略

线程池隔离策略(默认)

建立副线程去调用依赖服务, 执行依赖代码的线程与请求线程(好比Tomcat线程)分离 ,当副线程请求调用失败、超时等异常状况而阻塞时不会影响到主线程。算法

能够利用线程可设置超时的特性给经过网络进行的服务间调用设置超时时间。spring

线程池机制的优缺点数据库

线程池机制的优势:后端

(1) 任何一个依赖服务均可以被隔离在本身的线程池内,即便本身的线程池资源填满了,也不会影响任何其余的服务调用;缓存

(2) 服务能够随时引入一个新的依赖服务,由于即便这个新的依赖服务有问题,也不会影响其余任何服务的调用;tomcat

(3) 当一个故障的依赖服务从新变好的时候,能够经过清理掉线程池,瞬间恢复该服务的调用,而若是是tomcat线程池被占满,再恢复就很麻烦;服务器

(4) 若是一个client调用配置有问题,线程池的健康情况随时会报告,好比成功/失败/拒绝/超时的次数统计,而后能够近实时热修改依赖服务的调用配置,而不用停机;网络

(5) 若是一个服务自己发生了修改,须要从新调整配置,此时线程池的健康情况也能够随时发现,好比成功/失败/拒绝/超时的次数统计,而后能够近实时热修改依赖服务的调用配置,而不用停机;

(6) 基于线程池的异步本质,能够在同步的调用之上,构建一层异步调用层;

线程池机制的缺点:

(1) 线程池机制最大的缺点就是增长了cpu的开销。

信号量隔离策略

用于隔离本地代码,利用信号量限制同时运行的线程数量,不会建立副线程,开销较小(复杂算法、多重循环,时间复杂度高的状况)。

2、限流

hystrix利用线程池的工做原理来进行限流。

线程池的工做原理
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
  • corePoolSize 为线程池的基本大小。
  • maximumPoolSize 为线程池最大线程大小。
  • keepAliveTimeunit 则是线程空闲后的存活时间。
  • workQueue 用于存听任务的阻塞队列。
  • handler 当队列和最大线程池都满了以后的饱和策略。

188580-202ba87b6a285694.jpg

四种拒绝策略

2018110923282372.png

AbortPolicy:不处理,直接抛出异常。
CallerRunsPolicy:若线程池还没关闭,调用当前所在线程来运行任务,r.run()执行。
DiscardOldestPolicy:LRU策略,丢弃队列里最近最久不使用的一个任务,并执行当前任务。
DiscardPolicy:不处理,丢弃掉,不抛出异常。

3、主要参数配置

@HystrixCommand(commandKey = "getCompanyInfoById",
                    groupKey = "company-info",
                    threadPoolKey = "company-info",
                    fallbackMethod = "fallbackMethod",
                    threadPoolProperties = {
                        @HystrixProperty(name = "coreSize", value = "30"),
                        @HystrixProperty(name = "maxQueueSize", value = "101"),
                        @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                        @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"),
                     })
  • commandKey: 表明一个接口, 若是不配置,默认是@HystrixCommand注解修饰的函数的函数名。
  • groupKey: 表明一个服务,一个服务可能会暴露多个接口。 Hystrix会根据组来组织和统计命令的告、仪表盘等信息。Hystrix命令默认的线程划分也是根据命令组来实现。默认状况下,Hystrix会让相同组名的命令使用同一个线程池,因此咱们须要在建立Hystrix命令时为其指定命令组来实现默认的线程池划分。
  • threadPoolKey: 对线程池进行更细粒度的配置,默认等于groupKey的值。若是依赖服务中的某个接口耗时较长,须要单独特殊处理,最好单独用一个线程池,这时候就能够配置threadpool key。也能够多个服务接口设置同一个threadPoolKey构成线程组。
  • fallbackMethod:@HystrixCommand注解修饰的函数的回调函数,@HystrixCommand修饰的函数必须和这个回调函数定义在同一个类中,由于定义在了同一个类中,因此fackback method能够是public/private都可。
  • 线程池配置:coreSize表示核心线程数,hystrix默认是10;maxQueueSize表示线程池的最大队列大小; keepAliveTimeMinutes表示非核心线程空闲时最大存活时间;queueSizeRejectionThreshold:该参数用来为队列设置拒绝阈值。经过该参数,即便队列没有达到最大值也能拒绝请求。

4、降级

服务提早配置备用措施,当故障发生时无缝启用备用方案(或者返回一些默认值),用户无感知,最终目的都是为了提供7*24小时稳定服务,这样对用户来讲才是高价值、可信赖的优质服务。

线程run()抛出异常,超时,线程池或信号量满了,或短路了,都会触发fallback机制。

5、熔断

当请求调用失败率达到阀值自动触发降级(如因网络故障/超时形成的失败率高),熔断器触发的快速失败会进行快速恢复。

工做流程:

  1. 若是通过断路器的流量超过了必定的阈值,HystrixCommandProperties.circuitBreakerRequestVolumeThreshold()配置,默认20。好比,要求在10s内,通过短路器的流量必须达到20个才会去判断要不要短路;
  2. 若是断路器统计到的异常调用的占比超过了必定的阈值,HystrixCommandProperties.circuitBreakerErrorThresholdPercentage()配置。若是达到了上面的要求,好比说在10s内,通过短路器的请求达到了30个;同时其中异常的访问数量,占到了必定的比例(默认50%),好比60%的请求都是异常(报错,timeout,reject),会开启短路;
  3. 而后断路器从close状态转换到open状态;
  4. 断路器打开的时候,全部通过该断路器的请求所有被短路,不调用后端服务,直接走fallback降级逻辑;
  5. 通过了一段时间以后,HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()配置,断路器会half-open状态,让一条请求通过短路器,看能不能正常调用。若是调用成功了,那么断路器就自动恢复,转到close状态。

6、监控

根据监控结果能够看出合理的超时时长配置和计算出服务集群部署的规模大小。

u=2568138430,3468427254&fm=173&app=49&f=JPEG.jpg

计算公式:

服务集群部署的规模大小 = 服务在健康状态时每秒支撑的最大请求数 * 第99百分位延迟时间(以秒为单位)+ 少许用于缓冲的额外线程

举个例子:某个服务须要支撑的QPS为1000,即每秒须要处理1000个请求。假设经过统计获得百分之99的服务耗时为100ms,那一个线程1秒内能处理10个请求,一台机器的线程池大小通常就使用默认配置为10,即一台机器1秒内能处理 10 * 10 = 100个请求,最后得出该服务总共须要部署 1000/100 = 10台机器。

7、请求缓存、合并

请求缓存

在一次请求中,若是有多个command,参数都是同样的,调用的接口也是同样的,其实结果能够认为也是同样的。这个时候,可让第一次command执行返回的结果,被缓存在内存中,而后在这个请求上下文中,后续的其余对这个依赖的调用所有从内存中取用缓存结果就能够了。

https://blog.csdn.net/zhuchua...

请求合并

高并发的场景下,将一个时间窗口内多个相同请求合并成一个请求,较少网络链接次数。

http://blog.didispace.com/spr...

8、开发中遇到的问题

默认状况下,hystrix不会将父线程的上下文传播到有hystrix命令管理的线程中。例如,在默认状况下,对被父线程调用并由@HystrixCommand保护的方法而言,在父线程中设置为ThreadLocal的值是不会自动传递到子线程的。实际场景中的例子,调用通用UserUtil工具类从请求头获取不到userId, jwt串, 租户id。

两种方法:

(1) 手动设置

​ 手动从父线程ThreadLocal中取出须要的key-value设置到子线程中。

(2) 代码配置

​ 自定义HystrixConcurrencyStrategy

https://www.cnblogs.com/duanx...

相关文章
相关标签/搜索