九、 深入理解线程池隔离技术的设计原则

9.1 线程池隔离技术面对的场景

Hystrix采取了bulkhead舱壁隔离技术,来将外部依赖进行资源隔离,进而避免任何外部依赖的故障导致本服务崩溃(线程池隔离,学术名称:bulkhead,舱壁隔离)。外部依赖的调用在单独的线程中执行,这样就能跟调用线程隔离开来,避免外部依赖调用timeout耗时过长,导致调用线程被卡死

 

Hystrix对每个外部依赖用一个单独的线程池,这样的话,如果对那个外部依赖调用延迟很严重,最多就是耗尽那个依赖自己的线程池而已,不会影响其他的依赖调用

 

Hystrix选择用线程池机制来进行资源隔离,要面对的场景如下:

(1)每个服务都会调用几十个后端依赖服务,那些后端依赖服务通常是由很多不同的团队开发的。

(2)每个后端依赖服务都会提供它自己的client调用库,比如说用thrift的话,就会提供对应的thrift依赖。

(3)client调用库随时会变更。

(4)client调用库随时可能会增加新的网络请求的逻辑。

(5)client调用库可能会包含诸如自动重试,数据解析,内存中缓存等逻辑。

(6)client调用库一般都对调用者来说是个黑盒,包括实现细节,网络访问,默认配置,等等。

(7)在真实的生产环境中,经常会出现调用者,突然发现,client调用库发生了某些变化。

(8)即使client调用库没有改变,依赖服务本身可能有会发生逻辑上的变化。

(9)有些依赖的client调用库可能还会拉取其他的依赖库,而且可能那些依赖库配置的不正确。

(10)大多数网络请求都是同步调用的。

(11)调用失败和延迟,也有可能会发生在client调用库本身的代码中,不一定就是发生在网络请求中。

 

9.2 线程池机制的优点

(1)任何一个依赖服务都可以被隔离在自己的线程池内,即使自己的线程池资源填满了,也不会影响任何其他的服务调用

(2)服务可以随时引入一个新的依赖服务,因为即使这个新的依赖服务有问题,也不会影响其他任何服务的调用

(3)当一个故障的依赖服务重新变好的时候,可以通过清理掉线程池,瞬间恢复该服务的调用,而如果是tomcat线程池被占满,再恢复就很麻烦

(4)如果一个client调用库配置有问题,线程池的健康状况随时会报告,比如成功/失败/拒绝/超时的次数统计,然后可以近实时热修改依赖服务的调用配置,而不用停机

(5)如果一个服务本身发生了修改,需要重新调整配置,此时线程池的健康状况也可以随时发现,比如成功/失败/拒绝/超时的次数统计,然后可以近实时热修改依赖服务的调用配置,而不用停机

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

简单来说,最大的好处,就是资源隔离,确保说,任何一个依赖服务故障,不会拖垮当前的这个服务

 

9.3 线程池机制的缺点

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

除了tomcat本身的调用线程之外,还有hystrix自己管理的线程池

(2)每个command的执行都依托一个独立的线程,会进行排队,调度,还有上下文切换

(3)Hystrix官方自己做了一个多线程异步带来的额外开销,通过对比多线程异步调用+同步调用得出,Netflix API每天通过hystrix执行10亿次调用,每个服务实例有40个以上的线程池,每个线程池有10个左右的线程

(4)最后发现说,用hystrix的额外开销,就是给请求带来了3ms左右的延时,最多延时在10ms以内,相比于可用性和稳定性的提升,这是可以接受的

 

我们可以用hystrix semaphore技术来实现对某个依赖服务的并发访问量的限制,而不是通过线程池/队列的大小来限制流量

 

sempahore技术可以用来限流和削峰,但是不能用来对调研延迟的服务进行timeout和隔离

execution.isolation.strategy,设置为SEMAPHORE,那么hystrix就会用semaphore机制来替代线程池机制,来对依赖服务的访问进行限流

如果通过semaphore调用的时候,底层的网络调用延迟很严重,那么是无法timeout的,只能一直block住。

一旦请求数量超过了semephore限定的数量之后,就会立即开启限流

 

9.4 线程池接口限流参数设置

withCoreSize:设置你的线程池的大小

withMaxQueueSize:设置的是你的等待队列,缓冲队列的大小

withQueueSizeRejectionThreshold:如果withMaxQueueSize<withQueueSizeRejectionThreshold,那么取的是withMaxQueueSize,反之,取得是withQueueSizeRejectionThreshold

线程池本身的大小,如果你不设置另外两个queue相关的参数,等待队列是关闭的

.andThreadPoolPropertiesDefaults(

                            HystrixThreadPoolProperties.Setter()

                            //CoreSize:设置线程池的大小,默认是10,如果你不设置另外两个queue相关的参数,等待队列是关闭

                            //先进去线程池的是10个请求,5个请求进入等待队列,线程池里有空闲,等待队列中的请求如果还没有timeout,那么就进去线程池去执行

                            .withCoreSize(10)

                            //设置线程池的最大大小,只有在设置allowMaximumSizeToDivergeFromCoreSize的时候才能生效

                            .withMaximumSize(18)

                            //允许线程池大小自动动态调整(默认为false),设置为true之后,maxSize就生效了,此时如果一开始是coreSize个线程,随着并发量上来,那么就会自动获取新的线程,

                            //但是如果线程在keepAliveTimeMinutes内空闲,就会被自动释放掉

                            .withAllowMaximumSizeToDivergeFromCoreSize(true)

                            //如果coreSize < maxSize,那么这个参数就设置了一个线程多长时间空闲之后,就会被释放掉

                            .withKeepAliveTimeMinutes(1)

                            //设置的是你的等待队列,缓冲队列的大小

                            .withMaxQueueSize(12)

                            //QueueSizeRejectionThreshold:控制队列的最大大小HystrixCommand在提交到线程池之前,其实会先进入一个队列中,这个队列满了之后,才会reject

                            //如果withMaxQueueSize<withQueueSizeRejectionThreshold,那么取的是withMaxQueueSize,反之,取得是withQueueSizeRejectionThreshold

                           .withQueueSizeRejectionThreshold(8) queue生效

 

 

 

withExecutionTimeoutInMilliseconds(20000):手动设置timeout时长,timeout也设置大一些,否则如果请求放等待队列中时间太长了,直接就会timeout,等不到去线程池里执行了

withFallbackIsolationSemaphoreMaxConcurrentRequests(30):fallback,sempahore限流,30个,避免太多的请求同时调用fallback被拒绝访问

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章