【SpringCloud】HystrixCommand的threadPoolKey默认值及线程池初始化

关于threadPoolKey默认值的疑问

使用SpingCloud必然会用到Hystrix做熔断降级,也必然会用到@HystrixCommand注解,@HystrixCommand注解可以配置的除了常用的groupKey、commandKey、fallbackMethod等,还有一个很关键的就是threadPoolKey,就是使用Hystrix线程隔离策略时的线程池Key

/** * This annotation used to specify some methods which should be processes as hystrix commands. */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface 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 ""; ......省略 }

而使用中我们常常只指定fallbackMethod回退方法,而不会指定所有属性,从@HystrixCommand的源码注释来看

groupKey的默认值是使用@HystrixCommand标注的方法所在的类名

commandKey的默认值是@HystrixCommand标注的方法名,即每个方法会被当做一个HystrixCommand

threadPoolKey没有默认值

但threadPoolKey却没有说明默认值,而threadPoolKey是和执行HystrixCommand的线程池直接相关的

所以我的疑问就是,threadPoolKey有默认值吗? 默认值是什么? 执行HystrixCommand的线程池又是怎么初始化的? 可以动态调整吗?

测试论证 测试代码

spring-cloud-example-consumer-ribbon-hystrix-threadpool

测试端点及方式

首先需要启动 spring-cloud-example-eureka-server-standalone注册中心 和 spring-cloud-example-simple-provider服务提供者

再启动 spring-cloud-example-consumer-ribbon-hystrix-threadpool

测试端点

:20006/testDefaultThreadPoolKey

:20006/testDefaultThreadPoolKey2

testDefaultThreadPoolKey 和 testDefaultThreadPoolKey2 是同一个service的两个方法,分别使用@HystrixCommand指定fallback方法

线程池大小设置为2,且不使用队列暂存,服务提供方sleep 30秒,通过chrome多窗口调用 /testDefaultThreadPoolKey 端点

或同时调用 /testDefaultThreadPoolKey、/testDefaultThreadPoolKey2 端点
通过大于线程池最大值的请求被线程池拒绝进入fallback,判断线程池是方法级,还是类级的,以及threadPoolKey默认值

注意:

使用firefox浏览器测试有问题,多标签页必须等待一个GET请求完成,才能继续下一个,测不出并发的效果。

一开始以为是程序上的限制,后来才发现是浏览器,使用chrome问题解决

线程池配置 hystrix.threadpool.default.coreSize = 2 hystrix.threadpool.default.maximumSize = 2 hystrix.threadpool.default.maxQueueSize = -1

线程池的coreSize和maximumSize都设置为2(1.5.9版本后才添加maximumSize),且线程池队列大小为-1,即使用SynchronousQueue

Hystrix线程池的其它属性:

测试结果

【SpringCloud】HystrixCommand的threadPoolKey默认值及线程池初始化

chrome浏览器连续GET请求调用6次,分别调用3次 /testDefaultThreadPoolKey,3次 /testDefaultThreadPoolKey2,以绿色线问分隔,可见前两次调用成功,后4次均直接拒绝,进入fallback

具体异常信息为:

java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@6d72dcf rejected from java.util.concurrent.ThreadPoolExecutor@431bfebc[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 1]

线程池大小为2起到了作用,将大于并发数的请求拒绝了,并且无论是只调用 /testDefaultThreadPoolKey,还是轮询调用 /testDefaultThreadPoolKey 和 /testDefaultThreadPoolKey2 ,测试结果都是这样。再根据hystrix线程的名字 hystrix-ConsumerRibbonHystrixThreadPoolService-n,可以猜想:Hystrix的 threadPoolKey是和hystrixCommand执行的类相关的,可能一个类使用一个线程池,所以两个service方法才会共用线程池

原理分析 HystrixCommandAspect

首先,被@HystrixCommand注解标注的方法会被AOP拦截,具体逻辑在 HystrixCommandAspect

private static final Map<HystrixPointcutType, MetaHolderFactory> META_HOLDER_FACTORY_MAP; // 初始化用于处理 HystrixCommand 和 HystrixCollapser 的 MetaHolderFactory // HystrixCommand -- CommandMetaHolderFactory // HystrixCollapser -- CollapserMetaHolderFactory static { META_HOLDER_FACTORY_MAP = ImmutableMap.<HystrixPointcutType, MetaHolderFactory>builder() .put(HystrixPointcutType.COMMAND, new CommandMetaHolderFactory()) .put(HystrixPointcutType.COLLAPSER, new CollapserMetaHolderFactory()) .build(); } // HystrixCommand Pointcut切入点 @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)") public void hystrixCommandAnnotationPointcut() { } // HystrixCollapser Pointcut切入点 @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser)") public void hystrixCollapserAnnotationPointcut() { } // HystrixCommand 和 HystrixCollapser 的环绕通知 @Around("hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()") public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable { Method method = getMethodFromTarget(joinPoint); Validate.notNull(method, "failed to get method from joinPoint: %s", joinPoint); if (method.isAnnotationPresent(HystrixCommand.class) && method.isAnnotationPresent(HystrixCollapser.class)) { throw new IllegalStateException("method cannot be annotated with HystrixCommand and HystrixCollapser " + "annotations at the same time"); } // 创建metaHolder,用于保存元数据 MetaHolderFactory metaHolderFactory = META_HOLDER_FACTORY_MAP.get(HystrixPointcutType.of(method)); MetaHolder metaHolder = metaHolderFactory.create(joinPoint); // 创建HystrixCommand,HystrixInvokable是父接口 HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder); ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ? metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType(); // 执行HystrixCommand Object result; try { if (!metaHolder.isObservable()) { result = CommandExecutor.execute(invokable, executionType, metaHolder); } else { result = executeObservable(invokable, executionType, metaHolder); } } catch (HystrixBadRequestException e) { throw e.getCause() != null ? e.getCause() : e; } catch (HystrixRuntimeException e) { throw hystrixRuntimeExceptionToThrowable(metaHolder, e); } return result; }

重点是methodsAnnotatedWithHystrixCommand()环绕通知的实现方法

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygxjz.html