Hystrix依赖隔离之线程池隔离( 二 )


虽然Hystrix可以为每个依赖建立一个线程池 , 但是如果依赖成千上万 , 建立那么多线程池肯定是不可能的 。所以默认情况下 , Hystrix会为每一个Command Group建立一个线程池 。
5.Command Thread PoolHystrix可以指定创建或关联上一个线程池 , 每一个线程池都有一个Key 。这个线程池就是线程隔离的关键 , 所有的监控、缓存、调用等等都来自于这个线程池 。可以通过如下代码指定线程池:
public HelloWorldCommand(String name){//定义命令组 和 方法调用超时时间super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HelloWorldGroup")).andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorldCommand")).andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")));this.name = name;}上文说到 , 默认情况下 , 每一个Command Group会自动创建一个线程池 。那什么时候我们需要单独指定线程池呢?因为线程池主要的目的是隔离 , 所以当有一些依赖在一个Command Group中 , 但是又有隔离的必要的时候 , 比如一个依赖的超时会用满所有的线程池线程 , 而不应该影响其他的依赖 。
6.基本实现原理Command模式:Hystrix中大量使用rxJAVA来实现Command模式 。所有自定义的Command , 不管继承于HystrixObservableCommand还是HystrixCommand,最终都继承于AbstractCommand 。Thread Pool , Command Group , Command Key都在AbstractCommand这里实现 。
线程池的创建和管理:Hystrix的线程池在
HystrixConcurrencyStrategy初始化 , 线程池是由ThreadPoolExecutor实现的 。每个线程池默认初始化10个线程 。Hystrix有个静态类Factory , 创建的线程池会被存储在Factory中的ConcurrentHashMap中 。ConcurrentHashMap的Key则是上文说到的CommandGroupKey或者指定的ThreadPoolKey 。每次命令执行的时候 , 都会根据ThreadPoolKey去找到对应的线程池 。线程池拥有一个继承于rxjava中Scheduler的HystrixContextScheduler , 用于在执行命令的时候 , 把命令在这个线程池上调度执行 。
命令的执行:以execute()方法为例 , Hystrix通过toObservable()来构造命令 , 构造过程中 , 定义了整个命令执行过程中的stage(未开始、执行中、完成执行、执行异常等等)的回调和处理方法 。在
executeCommandWithSpecifiedIsolation()方法中 , 使用exjava的subscribeOn方法 , 传入上文提到的HystrixContextScheduler对象 , 通过HystrixContextScheduler的ThreadPoolScheduler把命令submit到ThreadPoolExecutor中去执行 。
7.最佳实践对于那些本来延迟就比较小的请求(例如访问本地缓存成功率很高的请求)来说 , 线程池带来的开销是非常高的 , 这时 , 你可以考虑采用其他方法 , 例如非阻塞信号量(不支持超时) , 来实现依赖服务的隔离 , 使用信号量的开销很小 。但绝大多数情况下 , Netflix 更偏向于使用线程池来隔离依赖服务 , 因为其带来的额外开销可以接受 , 并且能支持包括超时在内的所有功能 。




推荐阅读