# 092. hystrix 的线程池+服务+接口划分以及资源池的容量大小控制

资源隔离的两种策略:线程池和信号量

本章对资源隔离进一步讲解,进行细粒度的控制配置

# execution.isolation.strategy 隔离策略配置

  • THREAD 线程池机制:每个 command 运行在一个线程中,限流是通过线程池的大小来控制的
  • SEMAPHORE 信号量机制:command 是运行在调用线程中,但是通过信号量的容量来进行限流

如何在线程池和信号量之间做选择?

默认的策略就是线程池

  • 线程池

    最大的好处就是对于网络访问请求,如果有超时的话,可以避免调用线程阻塞住

  • 信号量

    通常是针对超大并发量的场景下,每个服务实例每秒都几百的 QPS,那么此时你用线程池的话,线程一般不会太多,可能撑不住那么高的并发,如果要撑住,可能要耗费大量的线程资源,那么就是用信号量,来进行限流保护(这种理解也能说得通)

    一般用信号量常见于那种基于纯内存的一些业务逻辑服务,而不涉及到任何网络访问请求

    netflix 有 100+ 的 command 运行在 40+ 的线程池中,只有少数 command 是不运行在线程池中的,就是从纯内存中获取一些元数据,或者是对多个 command 包装起来的 facacde command,是用信号量限流的

super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GetProductCommandGroup"))
        .andCommandPropertiesDefaults(
                HystrixCommandProperties.Setter()
                        .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
        )
);
1
2
3
4
5
6

# command 名称和 command 组

主要用来更细粒度的控制依赖服务接口线程池如何来划分

super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GetProductCommandGroup"))
        .andCommandKey(HystrixCommandKey.Factory.asKey("GetProductCommand"))
);
1
2
3

command group 是一个非常重要的概念,默认情况下,是通过 command group 来定义一个线程池的,而且还会通过 command group 来聚合一些监控和报警信息,同一个 command group 中的请求,都会进入同一个线程池中

# command 线程池

threadpool key 代表了一个 HystrixThreadPool,用来进行统一监控、统计、缓存,默认的 threadpool key 就是 command group 名称;每个 command 都会跟它的 threadpool key 对应的 thread pool 绑定在一起

super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GetProductCommandGroup"))
        .andCommandKey(HystrixCommandKey.Factory.asKey("GetProductCommand"))
        .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool"))
);
1
2
3
4

# 三种 key 含义

  • command key:代表了一类 command,一般来说代表了底层的依赖服务的一个接口

  • group key:代表了某一个底层的依赖服务

    一个依赖服务可能会暴露出来多个接口,每个接口就是一个 command key

    在逻辑上去组织起来一堆 command key 的调用、统计信息、成功次数、timeout 超时次数、失败次数、可以看到某一个服务整体的一些访问情况

    一般来说,推荐是根据一个服务去划分出一个线程池,command key 默认都是属于同一个线程池的

    比如说你以一个服务为粒度,估算出来这个服务每秒的所有接口加起来的整体 QPS 在 100 左右, 该服务部署了 10 个实例。每个实例该 group key 服务给 10 个左右线程就可以了, 整个集群服务的 QPS 就是 10 * 10 = 100

    一般来说 group key 是用来在逻辑上组合一堆 command 的; 举个例子,对于一个服务中的某个功能模块来说,希望将这个功能模块内的所有 command 放在一个 group 中,那么在监控和报警的时候可以放一起看

    group key 对应了一个服务,但是这个服务暴露出来的几个接口,访问量很不一样,差异非常之大, 你可能就希望在这个服务 group key 内部的多个 command key,做一些细粒度的资源隔离, 那么久需要用到 threadpool key 了

  • threadpool key

    threadpool key 是 command key 的上层;层级结构是这样的:group key -> threadpool key -> command key

    逻辑上来说,多个 command key 属于一个 command group,在做统计的时候会放在一起统计

    每个 command key 有自己的线程池,每个接口有自己的线程池,去做资源隔离和限流

    但是对于 thread pool 资源隔离来说,可能是希望能够拆分的更加一致一些,比如在一个功能模块内,对不同的请求可以使用不同的 thread pool

    command group 一般来说,可以是对应一个服务,多个 command key 对应这个服务的多个接口,多个接口的调用共享同一个线程池

# coreSize

这是线程池的大小,默认是 10

super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GetProductCommandGroup"))
        .andThreadPoolPropertiesDefaults(
                HystrixThreadPoolProperties.Setter()
                        .withCoreSize(10)
        )
);
1
2
3
4
5
6

一般来说,用这个默认的10个线程大小就够了

# queueSizeRejectionThreshold

该配置涉及到一个工作原理,这里画图简单讲解下

控制 queue 满后 reject(拒绝) 的 threshold,因为 maxQueueSize 不允许热修改,因此提供这个参数可以热修改,控制队列的最大大小

super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GetProductCommandGroup"))
        .andThreadPoolPropertiesDefaults(
                HystrixThreadPoolProperties.Setter()
                        .withCoreSize(10)
                        // 不能热修改
                        // com.netflix.hystrix.HystrixThreadPoolProperties.maxQueueSize 源码中有注释说明
                        .withMaxQueueSize(20)
                        // 可以热修改,默认值是 5
                        .withQueueSizeRejectionThreshold(10)
        )
);
1
2
3
4
5
6
7
8
9
10
11

# execution.isolation.semaphore.maxConcurrentRequests

设置使用 SEMAPHORE 隔离策略的时候,允许访问的最大并发量,超过这个最大并发量,请求直接被 reject

这个并发量的设置,跟线程池大小的设置,应该是类似的,但是基于信号量的话,性能会好很多,而且 hystrix 框架本身的开销会小很多

默认值是 10,设置的小一些,否则因为信号量是基于调用线程去执行 command 的,而且不能从 timeout 中抽离,因此一旦设置的太大,而且有延时发生,可能瞬间导致 tomcat 本身的线程资源本占满

不能从 timeout 中抽离是什么意思(前面章节私自提前了解了一点配置,实测有超时效果)?

super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GetProductCommandGroup"))
               // 超时时间
               .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                       .withExecutionTimeoutInMilliseconds(6000)
                       .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
                       // 信号量最大请求数量设置
                       .withExecutionIsolationSemaphoreMaxConcurrentRequests(2)
               )

       );
1
2
3
4
5
6
7
8
9
10