关于threadPoolKey默认值的疑问
原文链接:https://www.cnblogs.com/trust-freedom/p/9956427.html
使用SpingCloud必然会用到Hystrix做熔断降级,也必然会用到@HystrixCommand
注解,@HystrixCommand
注解可以配置的除了常用的groupKey、commandKey、fallbackMethod等,还有一个很关键的就是threadPoolKey,就是使用Hystrix线程隔离策略时的线程池Key
<span style="color:#000000"><code><span style="color:#008000">/**
* This annotation used to specify some methods which should be processes as hystrix commands.
*/</span>
<span style="color:#2b91af">@Target</span>({ElementType.METHOD})
<span style="color:#2b91af">@Retention</span>(RetentionPolicy.RUNTIME)
<span style="color:#2b91af">@Inherited</span>
<span style="color:#2b91af">@Documented</span>
<span style="color:#0000ff">public</span> <span style="color:#2b91af">@interface</span> HystrixCommand {
<span style="color:#008000">/**
* 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
*
* <span style="color:#808080">@return</span> group key
*/</span>
String <span style="color:#a31515">groupKey</span>() <span style="color:#0000ff">default</span> "";
<span style="color:#008000">/**
* Hystrix command key.
* <p/>
* default => the name of annotated method. for example:
* <code>
* ...
* <span style="color:#808080">@HystrixCommand</span>
* public User getUserById(...)
* ...
* the command name will be: 'getUserById'
* </code>
*
* <span style="color:#808080">@return</span> command key
*/</span>
String <span style="color:#a31515">commandKey</span>() <span style="color:#0000ff">default</span> "";
<span style="color:#008000">/**
* The thread-pool key is used to represent a
* HystrixThreadPool for monitoring, metrics publishing, caching and other such uses.
*
* <span style="color:#808080">@return</span> thread pool key
*/</span>
String <span style="color:#a31515">threadPoolKey</span>() <span style="color:#0000ff">default</span> "";
......省略
}</code></span>
而使用中我们常常只指定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
测试端点
http://127.0.0.1:20006/testDefaultThreadPoolKey
http://127.0.0.1:20006/testDefaultThreadPoolKey2
testDefaultThreadPoolKey 和 testDefaultThreadPoolKey2 是同一个service的两个方法,分别使用@HystrixCommand指定fallback方法
线程池大小设置为2,且不使用队列暂存,服务提供方sleep 30秒,通过chrome多窗口调用 /testDefaultThreadPoolKey 端点
或同时调用 /testDefaultThreadPoolKey、/testDefaultThreadPoolKey2 端点
通过大于线程池最大值的请求被线程池拒绝进入fallback,判断线程池是方法级,还是类级的,以及threadPoolKey默认值
注意:
使用firefox浏览器测试有问题,多标签页必须等待一个GET请求完成,才能继续下一个,测不出并发的效果。
一开始以为是程序上的限制,后来才发现是浏览器,使用chrome问题解决
线程池配置
<span style="color:#000000"><code>hystrix.threadpool.<span style="color:#0000ff">default</span>.coreSize = 2
hystrix.threadpool.<span style="color:#0000ff">default</span>.maximumSize = 2
hystrix.threadpool.<span style="color:#0000ff">default</span>.maxQueueSize = -1</code></span>
线程池的coreSize
和maximumSize
都设置为2(1.5.9版本后才添加maximumSize
),且线程池队列大小为-1,即使用SynchronousQueue
Hystrix线程池的其它属性: Hystrix Thread Pool Properties
测试结果
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
<span style="color:#000000"><code><span style="color:#0000ff">private</span> <span style="color:#0000ff">static</span> <span style="color:#0000ff">final</span> Map<HystrixPointcutType, MetaHolderFactory> META_HOLDER_FACTORY_MAP;
<span style="color:#008000">// 初始化用于处理 HystrixCommand 和 HystrixCollapser 的 MetaHolderFactory</span>
<span style="color:#008000">// HystrixCommand -- CommandMetaHolderFactory</span>
<span style="color:#008000">// HystrixCollapser -- CollapserMetaHolderFactory</span>
<span style="color:#0000ff">static</span> {
META_HOLDER_FACTORY_MAP = ImmutableMap.<HystrixPointcutType, MetaHolderFactory>builder()
.put(HystrixPointcutType.COMMAND, <span style="color:#0000ff">new</span> CommandMetaHolderFactory())
.put(HystrixPointcutType.COLLAPSER, <span style="color:#0000ff">new</span> CollapserMetaHolderFactory())
.build();
}
<span style="color:#008000">// HystrixCommand Pointcut切入点</span>
<span style="color:#2b91af">@Pointcut</span>(<span style="color:#a31515">"@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)"</span>)
<span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">hystrixCommandAnnotationPointcut</span>() {
}
<span style="color:#008000">// HystrixCollapser Pointcut切入点</span>
<span style="color:#2b91af">@Pointcut</span>(<span style="color:#a31515">"@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser)"</span>)
<span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">hystrixCollapserAnnotationPointcut</span>() {
}
<span style="color:#008000">// HystrixCommand 和 HystrixCollapser 的环绕通知</span>
<span style="color:#2b91af">@Around</span>(<span style="color:#a31515">"hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()"</span>)
<span style="color:#0000ff">public</span> Object <span style="color:#a31515">methodsAnnotatedWithHystrixCommand</span>(<span style="color:#0000ff">final</span> ProceedingJoinPoint joinPoint) <span style="color:#0000ff">throws</span> Throwable {
Method method = getMethodFromTarget(joinPoint);
Validate.notNull(method, <span style="color:#a31515">"failed to get method from joinPoint: %s"</span>, joinPoint);
<span style="color:#0000ff">if</span> (method.isAnnotationPresent(HystrixCommand.class) && method.isAnnotationPresent(HystrixCollapser.class)) {
<span style="color:#0000ff">throw</span> <span style="color:#0000ff">new</span> IllegalStateException(<span style="color:#a31515">"method cannot be annotated with HystrixCommand and HystrixCollapser "</span> +
<span style="color:#a31515">"annotations at the same time"</span>);
}
<span style="color:#008000">// 创建metaHolder,用于保存元数据</span>
MetaHolderFactory metaHolderFactory = META_HOLDER_FACTORY_MAP.get(HystrixPointcutType.of(method));
MetaHolder metaHolder = metaHolderFactory.create(joinPoint);
<span style="color:#008000">// 创建HystrixCommand,HystrixInvokable是父接口</span>
HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder);
ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ?
metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType();
<span style="color:#008000">// 执行HystrixCommand</span>
Object result;
<span style="color:#0000ff">try</span> {
<span style="color:#0000ff">if</span> (!metaHolder.isObservable()) {
result = CommandExecutor.execute(invokable, executionType, metaHolder);
} <span style="color:#0000ff">else</span> {
result = executeObservable(invokable, executionType, metaHolder);
}
} <span style="color:#0000ff">catch</span> (HystrixBadRequestException e) {
<span style="color:#0000ff">throw</span> e.getCause() != <span style="color:#0000ff">null</span> ? e.getCause() : e;
} <span style="color:#0000ff">catch</span> (HystrixRuntimeException e) {
<span style="color:#0000ff">throw</span> hystrixRuntimeExceptionToThrowable(metaHolder, e);
}
<span style="color:#0000ff">return</span> result;
}</code></span>
重点是methodsAnnotatedWithHystrixCommand()
环绕通知的实现方法
其中MetaHolder metaHolder = metaHolderFactory.create(joinPoint)
根据joinPoint的信息创建元数据时肯定会有初始化默认groupKey、默认commandKey以及默认threadPoolKey的逻辑
创建MetaHolder元数据
<span style="color:#000000"><code><span style="color:#0000ff">private</span> <span style="color:#0000ff">static</span> <span style="color:#0000ff">class</span> <span style="color:#a31515">CommandMetaHolderFactory</span> <span style="color:#0000ff">extends</span> <span style="color:#a31515">MetaHolderFactory</span> {
<span style="color:#2b91af">@Override</span>
<span style="color:#0000ff">public</span> MetaHolder <span style="color:#a31515">create</span>(Object proxy, Method method, Object obj, Object[] args, <span style="color:#0000ff">final</span> ProceedingJoinPoint joinPoint) {
HystrixCommand hystrixCommand = method.getAnnotation(HystrixCommand.class);
ExecutionType executionType = ExecutionType.getExecutionType(method.getReturnType());
<span style="color:#008000">// 创建MetaHolderBuilder</span>
MetaHolder.Builder builder = metaHolderBuilder(proxy, method, obj, args, joinPoint);
<span style="color:#0000ff">if</span> (isCompileWeaving()) {
builder.ajcMethod(getAjcMethodFromTarget(joinPoint));
}
<span style="color:#0000ff">return</span> builder.defaultCommandKey(method.getName()) <span style="color:#008000">//默认commandKey是方法名</span>
.hystrixCommand(hystrixCommand)
.observableExecutionMode(hystrixCommand.observableExecutionMode())
.executionType(executionType)
.observable(ExecutionType.OBSERVABLE == executionType)
.build();
}
}
<span style="color:#008000">//----------再来看看创建MetaHolderBuilder - metaHolderBuilder()</span>
<span style="color:#008000">//== MetaHolderFactory#metaHolderBuilder()</span>
MetaHolder.Builder <span style="color:#a31515">metaHolderBuilder</span>(Object proxy, Method method, Object obj, Object[] args, <span style="color:#0000ff">final</span> ProceedingJoinPoint joinPoint) {
MetaHolder.Builder builder = MetaHolder.builder()
.args(args).method(method).obj(obj).proxyObj(proxy)
.joinPoint(joinPoint);
<span style="color:#008000">// 设置fallback方法</span>
setFallbackMethod(builder, obj.getClass(), method);
<span style="color:#008000">// 设置默认配置</span>
builder = setDefaultProperties(builder, obj.getClass(), joinPoint);
<span style="color:#0000ff">return</span> builder;
}
<span style="color:#008000">//== 设置默认配置 setDefaultProperties()</span>
<span style="color:#0000ff">private</span> <span style="color:#0000ff">static</span> MetaHolder.Builder <span style="color:#a31515">setDefaultProperties</span>(MetaHolder.Builder builder, Class<?> declaringClass, <span style="color:#0000ff">final</span> ProceedingJoinPoint joinPoint) {
<span style="color:#008000">//根据@DefaultProperties注解获取配置</span>
Optional<DefaultProperties> defaultPropertiesOpt = AopUtils.getAnnotation(joinPoint, DefaultProperties.class);
<span style="color:#008000">//设置 默认groupKey为类名simpleName</span>
builder.defaultGroupKey(declaringClass.getSimpleName());
<span style="color:#008000">//如果存在@DefaultProperties,使用其指定的groupKey、threadPoolKey</span>
<span style="color:#0000ff">if</span> (defaultPropertiesOpt.isPresent()) {
DefaultProperties defaultProperties = defaultPropertiesOpt.get();
builder.defaultProperties(defaultProperties);
<span style="color:#0000ff">if</span> (StringUtils.isNotBlank(defaultProperties.groupKey())) {
builder.defaultGroupKey(defaultProperties.groupKey());
}
<span style="color:#0000ff">if</span> (StringUtils.isNotBlank(defaultProperties.threadPoolKey())) {
builder.defaultThreadPoolKey(defaultProperties.threadPoolKey());
}
}
<span style="color:#0000ff">return</span> builder;
}</code></span>
由此可见,在构造metaHolder元数据时,通过@HystrixCommand标准方法所在的类名作为goutpKey,通过方法名作为commandKey,但没有指定threadPoolKey
但执行HystrixCommand时是有默认threadPoolKey的,那么这个默认值从何而来,command又是怎么初始化线程池的呢??
通过metaHolder构造HystrixCommandBuilder
<span style="color:#000000"><code><span style="color:#008000">//----------HystrixCommandFactory#create()</span>
<span style="color:#0000ff">public</span> HystrixInvokable <span style="color:#a31515">create</span>(MetaHolder metaHolder) {
HystrixInvokable executable;
<span style="color:#0000ff">if</span> (metaHolder.isCollapserAnnotationPresent()) {
executable = <span style="color:#0000ff">new</span> CommandCollapser(metaHolder);
}
<span style="color:#0000ff">else</span> <span style="color:#0000ff">if</span> (metaHolder.isObservable()) {
executable = <span style="color:#0000ff">new</span> GenericObservableCommand(HystrixCommandBuilderFactory.getInstance().create(metaHolder));
}
<span style="color:#0000ff">else</span> {
<span style="color:#008000">//通过metaHolder构造HystrixCommandBuilder,再创建GenericCommand</span>
executable = <span style="color:#0000ff">new</span> GenericCommand(HystrixCommandBuilderFactory.getInstance().create(metaHolder));
}
<span style="color:#0000ff">return</span> executable;
}
<span style="color:#008000">//----------HystrixCommandBuilderFactory#create() 创建HystrixCommandBuilder</span>
<span style="color:#008000">//在创建HystrixCommandBuilder时,createGenericSetterBuilder(metaHolder)构造了Setter,是用于设置groupKey、commandKey、threadPoolKey的</span>
<span style="color:#0000ff">public</span> <ResponseType> HystrixCommandBuilder <span style="color:#a31515">create</span>(MetaHolder metaHolder, Collection<HystrixCollapser.CollapsedRequest<ResponseType, Object>> collapsedRequests) {
validateMetaHolder(metaHolder);
<span style="color:#0000ff">return</span> HystrixCommandBuilder.builder()
.setterBuilder(createGenericSetterBuilder(metaHolder)) <span style="color:#008000">//重点:设置setterBuilder</span>
.commandActions(createCommandActions(metaHolder))
.collapsedRequests(collapsedRequests)
.cacheResultInvocationContext(createCacheResultInvocationContext(metaHolder))
.cacheRemoveInvocationContext(createCacheRemoveInvocationContext(metaHolder))
.ignoreExceptions(metaHolder.getCommandIgnoreExceptions())
.executionType(metaHolder.getExecutionType())
.build();
}
<span style="color:#008000">//----------createGenericSetterBuilder() 创建SetterBuilder</span>
<span style="color:#0000ff">private</span> GenericSetterBuilder <span style="color:#a31515">createGenericSetterBuilder</span>(MetaHolder metaHolder) {
GenericSetterBuilder.Builder setterBuilder = GenericSetterBuilder.builder()
.groupKey(metaHolder.getCommandGroupKey())
.threadPoolKey(metaHolder.getThreadPoolKey()) <span style="color:#008000">//查看从metaHolder如何获取threadPoolKey</span>
.commandKey(metaHolder.getCommandKey())
.collapserKey(metaHolder.getCollapserKey())
.commandProperties(metaHolder.getCommandProperties())
.threadPoolProperties(metaHolder.getThreadPoolProperties())
.collapserProperties(metaHolder.getCollapserProperties());
<span style="color:#0000ff">if</span> (metaHolder.isCollapserAnnotationPresent()) {
setterBuilder.scope(metaHolder.getHystrixCollapser().scope());
}
<span style="color:#0000ff">return</span> setterBuilder.build();
}
<span style="color:#008000">//如果使用了Command注解,从注解指定的threadPoolKey 和 defaultThreadPoolKey二选一,以前者为主</span>
<span style="color:#008000">//本例中,既没有通过注解指定threadPoolKey,也没有defaultThreadPoolKey</span>
<span style="color:#0000ff">public</span> String <span style="color:#a31515">getThreadPoolKey</span>() {
<span style="color:#0000ff">return</span> isCommandAnnotationPresent() ? get(hystrixCommand.threadPoolKey(), defaultThreadPoolKey) : <span style="color:#a31515">""</span>;
}</code></span>
从上面看,HystrixCommandBuilder都构造完成了,还没有设置threadPoolKey
通过HystrixCommandBuilder创建HystrixCommand
下面是通过HystrixCommandBuilder
作为参数创建GenericCommand()
,即通过HystrixCommandBuilder
创建HystrixCommand
GenericCommand的类图为:
可见GenericCommand
集成关系,从AbstractHystrixCommand
--> HystrixCommand
--> AbstractCommand
,最终他们都是HystrixInvokeable
接口的实现了,即可被Hystrix调用的
向上进入父类构造,HystrixCommand(Setter setter)
<span style="color:#000000"><code><span style="color:#0000ff">protected</span> <span style="color:#a31515">HystrixCommand</span>(Setter setter) {
<span style="color:#008000">// use 'null' to specify use the default</span>
<span style="color:#0000ff">this</span>(setter.groupKey, setter.commandKey, setter.threadPoolKey, <span style="color:#0000ff">null</span>, <span style="color:#0000ff">null</span>, setter.commandPropertiesDefaults, setter.threadPoolPropertiesDefaults, <span style="color:#0000ff">null</span>, <span style="color:#0000ff">null</span>, <span style="color:#0000ff">null</span>, <span style="color:#0000ff">null</span>, <span style="color:#0000ff">null</span>);
}</code></span>
从setter中获取了groupKey、commandKey、threadPoolKey、commandPropertiesDefaults、threadPoolPropertiesDefaults,其它参数为null
AbstractCommand构造
进入到 AbstractCommand构造方法,封装了构造一个HystrixCommand的基本上所有元素的逻辑
<span style="color:#000000"><code><span style="color:#0000ff">protected</span> <span style="color:#a31515">AbstractCommand</span>(HystrixCommandGroupKey group, HystrixCommandKey key, HystrixThreadPoolKey threadPoolKey, HystrixCircuitBreaker circuitBreaker, HystrixThreadPool threadPool,
HystrixCommandProperties.Setter commandPropertiesDefaults, HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults,
HystrixCommandMetrics metrics, TryableSemaphore fallbackSemaphore, TryableSemaphore executionSemaphore,
HystrixPropertiesStrategy propertiesStrategy, HystrixCommandExecutionHook executionHook) {
<span style="color:#0000ff">this</span>.commandGroup = initGroupKey(group); <span style="color:#008000">//初始化commandGroupKey</span>
<span style="color:#0000ff">this</span>.commandKey = initCommandKey(key, getClass()); <span style="color:#008000">//初始化commandKey</span>
<span style="color:#0000ff">this</span>.properties = initCommandProperties(<span style="color:#0000ff">this</span>.commandKey, propertiesStrategy, commandPropertiesDefaults); <span style="color:#008000">//初始化commandProperties</span>
<span style="color:#0000ff">this</span>.threadPoolKey = initThreadPoolKey(threadPoolKey, <span style="color:#0000ff">this</span>.commandGroup, <span style="color:#0000ff">this</span>.properties.executionIsolationThreadPoolKeyOverride().get()); <span style="color:#008000">//初始化threadPoolKey</span>
<span style="color:#0000ff">this</span>.metrics = initMetrics(metrics, <span style="color:#0000ff">this</span>.commandGroup, <span style="color:#0000ff">this</span>.threadPoolKey, <span style="color:#0000ff">this</span>.commandKey, <span style="color:#0000ff">this</span>.properties); <span style="color:#008000">//初始化metrics</span>
<span style="color:#0000ff">this</span>.circuitBreaker = initCircuitBreaker(<span style="color:#0000ff">this</span>.properties.circuitBreakerEnabled().get(), circuitBreaker, <span style="color:#0000ff">this</span>.commandGroup, <span style="color:#0000ff">this</span>.commandKey, <span style="color:#0000ff">this</span>.properties, <span style="color:#0000ff">this</span>.metrics); <span style="color:#008000">//初始化断路器</span>
<span style="color:#0000ff">this</span>.threadPool = initThreadPool(threadPool, <span style="color:#0000ff">this</span>.threadPoolKey, threadPoolPropertiesDefaults); <span style="color:#008000">//初始化线程池</span>
<span style="color:#008000">//Strategies from plugins</span>
<span style="color:#0000ff">this</span>.eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
<span style="color:#0000ff">this</span>.concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
HystrixMetricsPublisherFactory.createOrRetrievePublisherForCommand(<span style="color:#0000ff">this</span>.commandKey, <span style="color:#0000ff">this</span>.commandGroup, <span style="color:#0000ff">this</span>.metrics, <span style="color:#0000ff">this</span>.circuitBreaker, <span style="color:#0000ff">this</span>.properties);
<span style="color:#0000ff">this</span>.executionHook = initExecutionHook(executionHook);
<span style="color:#0000ff">this</span>.requestCache = HystrixRequestCache.getInstance(<span style="color:#0000ff">this</span>.commandKey, <span style="color:#0000ff">this</span>.concurrencyStrategy);
<span style="color:#0000ff">this</span>.currentRequestLog = initRequestLog(<span style="color:#0000ff">this</span>.properties.requestLogEnabled().get(), <span style="color:#0000ff">this</span>.concurrencyStrategy);
<span style="color:#008000">/* fallback semaphore override if applicable */</span>
<span style="color:#0000ff">this</span>.fallbackSemaphoreOverride = fallbackSemaphore;
<span style="color:#008000">/* execution semaphore override if applicable */</span>
<span style="color:#0000ff">this</span>.executionSemaphoreOverride = executionSemaphore;
}</code></span>
接下来主要看是如何初始化threadPoolKey,以及threadPool的
initThreadPoolKey()
initThreadPoolKey(threadPoolKey, this.commandGroup, this.properties.executionIsolationThreadPoolKeyOverride().get())
参数:
-
threadPoolKey -- 指定的 或 默认的threadPoolKey
-
this.commandGroup -- 当前的groupKey
-
this.properties.executionIsolationThreadPoolKeyOverride().get()) -- 字符串类型,允许动态覆盖修改HystrixThreadPoolKey的值,并将动态更新HystrixCommand执行的HystrixThreadPool,这个override值的典型值是null,并且在构造HystrixCommandProperties时override全局的配置为null
// threadpool doesn't have a global override, only instance level makes sense this.executionIsolationThreadPoolKeyOverride = forString().add(propertyPrefix + ".command." + key.name() + ".threadPoolKeyOverride", null).build();
接着看 initThreadPoolKey() 方法内部
<span style="color:#000000"><code><span style="color:#008000">/*
* ThreadPoolKey
*
* This defines which thread-pool this command should run on.
*
* It uses the HystrixThreadPoolKey if provided, then defaults to use HystrixCommandGroup.
* 如果提供了threadPoolKey,就使用,否则默认使用groupKey
*
* It can then be overridden by a property if defined so it can be changed at runtime.
* 可以被threadPoolKeyOverride在运行时动态覆盖
*/</span>
<span style="color:#0000ff">private</span> <span style="color:#0000ff">static</span> HystrixThreadPoolKey <span style="color:#a31515">initThreadPoolKey</span>(HystrixThreadPoolKey threadPoolKey, HystrixCommandGroupKey groupKey, String threadPoolKeyOverride) {
<span style="color:#0000ff">if</span> (threadPoolKeyOverride == <span style="color:#0000ff">null</span>) {
<span style="color:#008000">// we don't have a property overriding the value so use either HystrixThreadPoolKey or HystrixCommandGroup</span>
<span style="color:#0000ff">if</span> (threadPoolKey == <span style="color:#0000ff">null</span>) {
<span style="color:#008000">/*
* use HystrixCommandGroup if HystrixThreadPoolKey is null
* 如果HystrixThreadPoolKey为空,使用groupKey作为threadPoolKey
*/</span>
<span style="color:#0000ff">return</span> HystrixThreadPoolKey.Factory.asKey(groupKey.name());
} <span style="color:#0000ff">else</span> {
<span style="color:#0000ff">return</span> threadPoolKey;
}
} <span style="color:#0000ff">else</span> {
<span style="color:#008000">// we have a property defining the thread-pool so use it instead</span>
<span style="color:#0000ff">return</span> HystrixThreadPoolKey.Factory.asKey(threadPoolKeyOverride);
}
}</code></span>
可见,在最开始构造HystrixCommand时,threadPoolKeyOverride为null,且没有自己指定的threadPoolKey,也没有默认的threadPoolKey,那么将使用groupKey作为threadPoolKey
所以,默认使用groupKey作为threadPoolKey,而group默认值是标注了@HystrixCommand的类名
最后,看一下如何根据threadPoolKey,初始化threadPool
initThreadPool()
<span style="color:#000000"><code><span style="color:#008000">//----------AbstractCommand#initThreadPool()</span>
<span style="color:#0000ff">private</span> <span style="color:#0000ff">static</span> HystrixThreadPool <span style="color:#a31515">initThreadPool</span>(HystrixThreadPool fromConstructor, HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults) {
<span style="color:#008000">// fromConstructor为null,使用HystrixThreadPool.Factory创建线程池</span>
<span style="color:#0000ff">if</span> (fromConstructor == <span style="color:#0000ff">null</span>) {
<span style="color:#008000">// get the default implementation of HystrixThreadPool</span>
<span style="color:#0000ff">return</span> HystrixThreadPool.Factory.getInstance(threadPoolKey, threadPoolPropertiesDefaults);
} <span style="color:#0000ff">else</span> {
<span style="color:#0000ff">return</span> fromConstructor;
}
}
<span style="color:#008000">//----------HystrixThreadPool.Factory#getInstance() 获取HystrixThreadPool实例</span>
<span style="color:#0000ff">static</span> HystrixThreadPool <span style="color:#a31515">getInstance</span>(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties.Setter propertiesBuilder) {
<span style="color:#008000">// get the key to use instead of using the object itself so that if people forget to implement equals/hashcode things will still work</span>
String key = threadPoolKey.name();
<span style="color:#008000">// this should find it for all but the first time</span>
<span style="color:#008000">// 从缓存threadPools中获取HystrixThreadPool,有则直接返回</span>
HystrixThreadPool previouslyCached = threadPools.get(key);
<span style="color:#0000ff">if</span> (previouslyCached != <span style="color:#0000ff">null</span>) {
<span style="color:#0000ff">return</span> previouslyCached;
}
<span style="color:#008000">// if we get here this is the first time so we need to initialize</span>
<span style="color:#008000">// 第一次初始化HystrixThreadPool</span>
<span style="color:#0000ff">synchronized</span> (HystrixThreadPool.class) {
<span style="color:#0000ff">if</span> (!threadPools.containsKey(key)) {
threadPools.put(key, <span style="color:#0000ff">new</span> HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder));
}
}
<span style="color:#0000ff">return</span> threadPools.get(key);
}</code></span>
先根据threadPoolKey尝试从threadPools这个ConcurrentHashMap<String, HystrixThreadPool>
中获取,即从线程池缓存中获取,有就直接返回previouslyCached之前的缓存,如果没有,synchromized对HystrixThreadPool类上锁后,再次判断还是没有threadPoolKey的缓存,就 new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder)
<span style="color:#000000"><code><span style="color:#008000">//----------new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder)</span>
<span style="color:#0000ff">public</span> <span style="color:#a31515">HystrixThreadPoolDefault</span>(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties.Setter propertiesDefaults) {
<span style="color:#0000ff">this</span>.properties = HystrixPropertiesFactory.getThreadPoolProperties(threadPoolKey, propertiesDefaults); <span style="color:#008000">//threadPoolProperties</span>
HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy(); <span style="color:#008000">//并发策略</span>
<span style="color:#0000ff">this</span>.queueSize = properties.maxQueueSize().get(); <span style="color:#008000">//线程池队列大小</span>
<span style="color:#008000">//创建HystrixThreadPoolMetrics,其中concurrencyStrategy.getThreadPool()会创建线程池</span>
<span style="color:#0000ff">this</span>.metrics = HystrixThreadPoolMetrics.getInstance(threadPoolKey,
concurrencyStrategy.getThreadPool(threadPoolKey, properties),
properties);
<span style="color:#0000ff">this</span>.threadPool = <span style="color:#0000ff">this</span>.metrics.getThreadPool();
<span style="color:#0000ff">this</span>.queue = <span style="color:#0000ff">this</span>.threadPool.getQueue();
<span style="color:#008000">/* strategy: HystrixMetricsPublisherThreadPool */</span>
HystrixMetricsPublisherFactory.createOrRetrievePublisherForThreadPool(threadPoolKey, <span style="color:#0000ff">this</span>.metrics, <span style="color:#0000ff">this</span>.properties);
}
<span style="color:#008000">//----------HystrixConcurrencyStrategy#getThreadPool(HystrixThreadPoolKey, HystrixThreadPoolProperties)</span>
<span style="color:#008000">// concurrencyStrategy.getThreadPool()时会创建ThreadPoolExecutor</span>
<span style="color:#0000ff">public</span> ThreadPoolExecutor <span style="color:#a31515">getThreadPool</span>(<span style="color:#0000ff">final</span> HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties threadPoolProperties) {
<span style="color:#0000ff">final</span> ThreadFactory threadFactory = getThreadFactory(threadPoolKey);
<span style="color:#0000ff">final</span> <span style="color:#0000ff">boolean</span> allowMaximumSizeToDivergeFromCoreSize = threadPoolProperties.getAllowMaximumSizeToDivergeFromCoreSize().get(); <span style="color:#008000">//是否允许maximumSize生效</span>
<span style="color:#0000ff">final</span> <span style="color:#0000ff">int</span> dynamicCoreSize = threadPoolProperties.coreSize().get(); <span style="color:#008000">//动态coreSize</span>
<span style="color:#0000ff">final</span> <span style="color:#0000ff">int</span> keepAliveTime = threadPoolProperties.keepAliveTimeMinutes().get(); <span style="color:#008000">//大于coreSize的线程,未使用的保活时间</span>
<span style="color:#0000ff">final</span> <span style="color:#0000ff">int</span> maxQueueSize = threadPoolProperties.maxQueueSize().get(); <span style="color:#008000">//线程队列最大值</span>
<span style="color:#0000ff">final</span> BlockingQueue<Runnable> workQueue = getBlockingQueue(maxQueueSize);
<span style="color:#008000">//允许使用maximumSize</span>
<span style="color:#0000ff">if</span> (allowMaximumSizeToDivergeFromCoreSize) {
<span style="color:#0000ff">final</span> <span style="color:#0000ff">int</span> dynamicMaximumSize = threadPoolProperties.maximumSize().get();
<span style="color:#008000">//dynamicCoreSize > dynamicMaximumSize,打印error</span>
<span style="color:#0000ff">if</span> (dynamicCoreSize > dynamicMaximumSize) {
logger.error(<span style="color:#a31515">"Hystrix ThreadPool configuration at startup for : "</span> + threadPoolKey.name() + <span style="color:#a31515">" is trying to set coreSize = "</span> +
dynamicCoreSize + <span style="color:#a31515">" and maximumSize = "</span> + dynamicMaximumSize + <span style="color:#a31515">". Maximum size will be set to "</span> +
dynamicCoreSize + <span style="color:#a31515">", the coreSize value, since it must be equal to or greater than the coreSize value"</span>);
<span style="color:#0000ff">return</span> <span style="color:#0000ff">new</span> ThreadPoolExecutor(dynamicCoreSize, dynamicCoreSize, keepAliveTime, TimeUnit.MINUTES, workQueue, threadFactory);
}
<span style="color:#008000">//dynamicCoreSize <= dynamicMaximumSize,正常</span>
<span style="color:#0000ff">else</span> {
<span style="color:#0000ff">return</span> <span style="color:#0000ff">new</span> ThreadPoolExecutor(dynamicCoreSize, dynamicMaximumSize, keepAliveTime, TimeUnit.MINUTES, workQueue, threadFactory);
}
}
<span style="color:#0000ff">else</span> { <span style="color:#008000">//不允许使用maximumSize</span>
<span style="color:#0000ff">return</span> <span style="color:#0000ff">new</span> ThreadPoolExecutor(dynamicCoreSize, dynamicCoreSize, keepAliveTime, TimeUnit.MINUTES, workQueue, threadFactory);
}
}</code></span>
至此,线程池创建完毕
结论
-
threadPoolKey的默认值是groupKey,而groupKey默认值是@HystrixCommand标注的方法所在类名
-
可以通过在类上加@DefaultProperties( threadPoolKey="xxx" )设置默认的threadPoolKey
-
可以通过@HystrixCommand( threadPoolKey="xxx" ) 指定当前HystrixCommand实例的threadPoolKey
-
threadPoolKey用于从线程池缓存中获取线程池 和 初始化创建线程池,由于默认以groupKey即类名为threadPoolKey,那么默认所有在一个类中的HystrixCommand共用一个线程池
-
动态配置线程池 -- 可以通过
hystrix.command.HystrixCommandKey.threadPoolKeyOverride=线程池key
动态设置threadPoolKey,对应的HystrixCommand所使用的线程池也会重新创建,还可以继续通过hystrix.threadpool.HystrixThreadPoolKey.coreSize=n
和hystrix.threadpool.HystrixThreadPoolKey.maximumSize=n
动态设置线程池大小注意: 通过threadPoolKeyOverride动态修改threadPoolKey之后,hystrixCommand会使用新的threadPool,但是老的线程池还会一直存在,并没有触发shutdown的机制