HystrixCommand使用及创建线程池问题
/*** This annotation used to specify some methods which should be processes as hystrix commands.*/({ElementType.METHOD})(RetentionPolicy.RUNTIME)public HystrixCommand {/*** The command group key is used for grouping together commands such as for reporting,* alerting, dashboards or team/library ownership.* <p/>* default => the runtime class name of annotated method** @return group key*/String groupKey() default "";/*** Hystrix command key.* <p/>* default => the name of annotated method. for example:* <code>* ...* @HystrixCommand* public User getUserById(...)* ...* the command name will be: 'getUserById'* </code>** @return command key*/String commandKey() default "";/*** The thread-pool key is used to represent a* HystrixThreadPool for monitoring, metrics publishing, caching and other such uses.** @return thread pool key*/String threadPoolKey() default "";......省略
1、groupKey的默认值是使用@HystrixCommand标注的方法所在的类名2、commandKey的默认值是@HystrixCommand标注的方法名,即每个方法会被当做一个HystrixCommand
3、threadPoolKey没有默认值,其实是和 groupK ey保持一致
由此可见,创建了很多的线程,而且,大部分线程处于等待、限时等待状态,使用率并不高。线程池也创建了很多,默认线程池名称为hystrix-类名-*,而且线程池中的线程都处于空闲状态,过多的线程白白消耗CPU的调度资源,会带来一定的开销。
在自己的HystrixCommand注解中可以指定groupKey/threadPoolKey使用哪个线程池,也可以指定commandKey使用哪个超时指令,还有fallbackMethod执行失败回调降级方法,当然还有commandProperties/threadPoolProperties这两个可以定制线程池参数和Command参数。
②、第二种可以java类注解和方法注解结合使用
如下:
在类上添加DefaultProperties注解
在方法上添加HystrixCommand注解
这样在方法上添加的HystrixCommand注解可以不添加任何属性,然后在类上@DefaultProperties注解上指定自己想要用的属性就行了,比如指定线程池,指定CommandKey等,而如果既在类上添加了@DefaultProperties注解以及其属性,又在方法上添加了@HystrixCommand及其属性,则以HystrixCommand为准。
以上是@HystrixCommand注解的使用说明,接下来简单介绍Hystrix线程池的创建。
在使用HystrixCommand注解后,如果我们想看Hystrix的源码,其中一个重要的切入点就是HystrixCommand的AOP切入逻辑,这块逻辑在HystrixCommandAspect这个类中,
被@HystrixCommand标注的方法都会被AOP拦截,在aop拦截逻辑中有获取groupKey、commandKey以及默认threadPoolKey和创建线程池等等逻辑。具体过程不在这里介绍了,直接看线程池的创建,线程池的创建最终会执行到AbstractCommand#initThreadPool()方法。
其源码逻辑大致如下:
private static HystrixThreadPool initThreadPool(HystrixThreadPool fromConstructor, HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults) {if (fromConstructor == null) {// get the default implementation of HystrixThreadPoolreturn HystrixThreadPool.Factory.getInstance(threadPoolKey, threadPoolPropertiesDefaults);} else {return fromConstructor;}}static HystrixThreadPool getInstance(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties.Setter propertiesBuilder) {// get the key to use instead of using the object itself so that if people forget to implement equals/hashcode things will still workString key = threadPoolKey.name();// this should find it for all but the first timeHystrixThreadPool previouslyCached = threadPools.get(key);if (previouslyCached != null) {return previouslyCached;}// if we get here this is the first time so we need to initializesynchronized (HystrixThreadPool.class) {if (!threadPools.containsKey(key)) {threadPools.put(key, new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder));}}return threadPools.get(key);}
public HystrixThreadPoolDefault(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties.Setter propertiesDefaults) {this.properties = HystrixPropertiesFactory.getThreadPoolProperties(threadPoolKey, propertiesDefaults); //threadPoolPropertiesHystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy(); //并发策略this.queueSize = properties.maxQueueSize().get(); //线程池队列大小//创建HystrixThreadPoolMetrics,其中concurrencyStrategy.getThreadPool()会创建线程池this.metrics = HystrixThreadPoolMetrics.getInstance(threadPoolKey,concurrencyStrategy.getThreadPool(threadPoolKey, properties),properties);this.threadPool = this.metrics.getThreadPool();this.queue = this.threadPool.getQueue();/* strategy: HystrixMetricsPublisherThreadPool */HystrixMetricsPublisherFactory.createOrRetrievePublisherForThreadPool(threadPoolKey, this.metrics, this.properties);}// concurrencyStrategy.getThreadPool()时会创建ThreadPoolExecutorpublic ThreadPoolExecutor getThreadPool(final HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties threadPoolProperties) {final ThreadFactory threadFactory = getThreadFactory(threadPoolKey);final boolean allowMaximumSizeToDivergeFromCoreSize = threadPoolProperties.getAllowMaximumSizeToDivergeFromCoreSize().get(); //是否允许maximumSize生效final int dynamicCoreSize = threadPoolProperties.coreSize().get(); //动态coreSizefinal int keepAliveTime = threadPoolProperties.keepAliveTimeMinutes().get(); //大于coreSize的线程,未使用的保活时间final int maxQueueSize = threadPoolProperties.maxQueueSize().get(); //线程队列最大值final BlockingQueue<Runnable> workQueue = getBlockingQueue(maxQueueSize);if (allowMaximumSizeToDivergeFromCoreSize) {final int dynamicMaximumSize = threadPoolProperties.maximumSize().get();if (dynamicCoreSize > dynamicMaximumSize) {logger.error("Hystrix ThreadPool configuration at startup for : " + threadPoolKey.name() + " is trying to set coreSize = " +dynamicCoreSize + " and maximumSize = " + dynamicMaximumSize + ". Maximum size will be set to " +dynamicCoreSize + ", the coreSize value, since it must be equal to or greater than the coreSize value");return new ThreadPoolExecutor(dynamicCoreSize, dynamicCoreSize, keepAliveTime, TimeUnit.MINUTES, workQueue, threadFactory);}else {return new ThreadPoolExecutor(dynamicCoreSize, dynamicMaximumSize, keepAliveTime, TimeUnit.MINUTES, workQueue, threadFactory);}}else {return new ThreadPoolExecutor(dynamicCoreSize, dynamicCoreSize, keepAliveTime, TimeUnit.MINUTES, workQueue, threadFactory);}}
至此,在这里就真正创建了JUC包下面的ThreadPoolExecutor线程池
其中我们需要特别注意一下这个抽象类HystrixConcurrencyStrategy
这个抽象类中有以下这些方法:
到这里我们应该感到高兴,其实这里是我们的一个扩展点:
我们在这里可以进行创建线程池的自定义操作、创建工作队列的自定义操作,以及线程执行前的一些自定义操作,也就是说可以根据我们的具体业务需求进行定制。
比如:
①、tid的透传
②、Hystrix线程池的监控接入
总之,是给我们留了可扩展的入手点。
四、总结
②、commandKey的默认值是@HystrixCommand标注的方法名,即每个方法会被当做一个HystrixCommand
③、threadPoolKey没有默认值,和groupKey保持一致
④、可以通过在类上加@DefaultProperties( threadPoolKey="xxx" )设置默认的threadPoolKey然后在方法上直接加@HystrixCommand
⑤、可以只通过@HystrixCommand( threadPoolKey="xxx" ) 指定当前HystrixCommand实例的threadPoolKey等其他属性
⑥、HystrixConcurrencyStrategy给我们提供了很多扩展点,可以根据业务需要进行定制扩展
以上是个人的亲身经历及总结经验,个人之见,难免考虑不全,如果大家有更好的建议欢迎大家私信留言。
如果觉得对你有一点点帮助,希望能够动动小手,你的点赞是对我最大的鼓励支持。
