关于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

测试端点

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问题解决

线程池配置

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线程池的其它属性: 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

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() 环绕通知的实现方法

其中MetaHolder metaHolder = metaHolderFactory.create(joinPoint)根据joinPoint的信息创建元数据时肯定会有初始化默认groupKey、默认commandKey以及默认threadPoolKey的逻辑

创建MetaHolder元数据

private static class CommandMetaHolderFactory extends MetaHolderFactory {
@Override
public MetaHolder create(Object proxy, Method method, Object obj, Object[] args, final ProceedingJoinPoint joinPoint) {
HystrixCommand hystrixCommand = method.getAnnotation(HystrixCommand.class);
ExecutionType executionType = ExecutionType.getExecutionType(method.getReturnType()); // 创建MetaHolderBuilder
MetaHolder.Builder builder = metaHolderBuilder(proxy, method, obj, args, joinPoint);
if (isCompileWeaving()) {
builder.ajcMethod(getAjcMethodFromTarget(joinPoint));
} return builder.defaultCommandKey(method.getName()) //默认commandKey是方法名
.hystrixCommand(hystrixCommand)
.observableExecutionMode(hystrixCommand.observableExecutionMode())
.executionType(executionType)
.observable(ExecutionType.OBSERVABLE == executionType)
.build();
}
} //----------再来看看创建MetaHolderBuilder - metaHolderBuilder()
//== MetaHolderFactory#metaHolderBuilder()
MetaHolder.Builder metaHolderBuilder(Object proxy, Method method, Object obj, Object[] args, final ProceedingJoinPoint joinPoint) {
MetaHolder.Builder builder = MetaHolder.builder()
.args(args).method(method).obj(obj).proxyObj(proxy)
.joinPoint(joinPoint); // 设置fallback方法
setFallbackMethod(builder, obj.getClass(), method);
// 设置默认配置
builder = setDefaultProperties(builder, obj.getClass(), joinPoint); return builder;
} //== 设置默认配置 setDefaultProperties()
private static MetaHolder.Builder setDefaultProperties(MetaHolder.Builder builder, Class<?> declaringClass, final ProceedingJoinPoint joinPoint) {
//根据@DefaultProperties注解获取配置
Optional<DefaultProperties> defaultPropertiesOpt = AopUtils.getAnnotation(joinPoint, DefaultProperties.class);
//设置 默认groupKey为类名simpleName
builder.defaultGroupKey(declaringClass.getSimpleName()); //如果存在@DefaultProperties,使用其指定的groupKey、threadPoolKey
if (defaultPropertiesOpt.isPresent()) {
DefaultProperties defaultProperties = defaultPropertiesOpt.get();
builder.defaultProperties(defaultProperties);
if (StringUtils.isNotBlank(defaultProperties.groupKey())) {
builder.defaultGroupKey(defaultProperties.groupKey());
}
if (StringUtils.isNotBlank(defaultProperties.threadPoolKey())) {
builder.defaultThreadPoolKey(defaultProperties.threadPoolKey());
}
}
return builder;
}

由此可见,在构造metaHolder元数据时,通过@HystrixCommand标准方法所在的类名作为goutpKey,通过方法名作为commandKey,但没有指定threadPoolKey

但执行HystrixCommand时是有默认threadPoolKey的,那么这个默认值从何而来,command又是怎么初始化线程池的呢??

通过metaHolder构造HystrixCommandBuilder

//----------HystrixCommandFactory#create()
public HystrixInvokable create(MetaHolder metaHolder) {
HystrixInvokable executable;
if (metaHolder.isCollapserAnnotationPresent()) {
executable = new CommandCollapser(metaHolder);
}
else if (metaHolder.isObservable()) {
executable = new GenericObservableCommand(HystrixCommandBuilderFactory.getInstance().create(metaHolder));
}
else {
//通过metaHolder构造HystrixCommandBuilder,再创建GenericCommand
executable = new GenericCommand(HystrixCommandBuilderFactory.getInstance().create(metaHolder));
}
return executable;
} //----------HystrixCommandBuilderFactory#create() 创建HystrixCommandBuilder
//在创建HystrixCommandBuilder时,createGenericSetterBuilder(metaHolder)构造了Setter,是用于设置groupKey、commandKey、threadPoolKey的
public <ResponseType> HystrixCommandBuilder create(MetaHolder metaHolder, Collection<HystrixCollapser.CollapsedRequest<ResponseType, Object>> collapsedRequests) {
validateMetaHolder(metaHolder); return HystrixCommandBuilder.builder()
.setterBuilder(createGenericSetterBuilder(metaHolder)) //重点:设置setterBuilder
.commandActions(createCommandActions(metaHolder))
.collapsedRequests(collapsedRequests)
.cacheResultInvocationContext(createCacheResultInvocationContext(metaHolder))
.cacheRemoveInvocationContext(createCacheRemoveInvocationContext(metaHolder))
.ignoreExceptions(metaHolder.getCommandIgnoreExceptions())
.executionType(metaHolder.getExecutionType())
.build();
} //----------createGenericSetterBuilder() 创建SetterBuilder
private GenericSetterBuilder createGenericSetterBuilder(MetaHolder metaHolder) {
GenericSetterBuilder.Builder setterBuilder = GenericSetterBuilder.builder()
.groupKey(metaHolder.getCommandGroupKey())
.threadPoolKey(metaHolder.getThreadPoolKey()) //查看从metaHolder如何获取threadPoolKey
.commandKey(metaHolder.getCommandKey())
.collapserKey(metaHolder.getCollapserKey())
.commandProperties(metaHolder.getCommandProperties())
.threadPoolProperties(metaHolder.getThreadPoolProperties())
.collapserProperties(metaHolder.getCollapserProperties());
if (metaHolder.isCollapserAnnotationPresent()) {
setterBuilder.scope(metaHolder.getHystrixCollapser().scope());
}
return setterBuilder.build();
} //如果使用了Command注解,从注解指定的threadPoolKey 和 defaultThreadPoolKey二选一,以前者为主
//本例中,既没有通过注解指定threadPoolKey,也没有defaultThreadPoolKey
public String getThreadPoolKey() {
return isCommandAnnotationPresent() ? get(hystrixCommand.threadPoolKey(), defaultThreadPoolKey) : "";
}

从上面看,HystrixCommandBuilder都构造完成了,还没有设置threadPoolKey

通过HystrixCommandBuilder创建HystrixCommand

下面是通过HystrixCommandBuilder作为参数创建GenericCommand(),即通过HystrixCommandBuilder创建HystrixCommand

GenericCommand的类图为:

可见GenericCommand集成关系,从AbstractHystrixCommand --> HystrixCommand --> AbstractCommand,最终他们都是HystrixInvokeable接口的实现了,即可被Hystrix调用的

向上进入父类构造,HystrixCommand(Setter setter)

protected HystrixCommand(Setter setter) {
// use 'null' to specify use the default
this(setter.groupKey, setter.commandKey, setter.threadPoolKey, null, null, setter.commandPropertiesDefaults, setter.threadPoolPropertiesDefaults, null, null, null, null, null);
}

从setter中获取了groupKey、commandKey、threadPoolKey、commandPropertiesDefaults、threadPoolPropertiesDefaults,其它参数为null

AbstractCommand构造

进入到 AbstractCommand构造方法,封装了构造一个HystrixCommand的基本上所有元素的逻辑

protected AbstractCommand(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) { this.commandGroup = initGroupKey(group); //初始化commandGroupKey
this.commandKey = initCommandKey(key, getClass()); //初始化commandKey
this.properties = initCommandProperties(this.commandKey, propertiesStrategy, commandPropertiesDefaults); //初始化commandProperties
this.threadPoolKey = initThreadPoolKey(threadPoolKey, this.commandGroup, this.properties.executionIsolationThreadPoolKeyOverride().get()); //初始化threadPoolKey
this.metrics = initMetrics(metrics, this.commandGroup, this.threadPoolKey, this.commandKey, this.properties); //初始化metrics
this.circuitBreaker = initCircuitBreaker(this.properties.circuitBreakerEnabled().get(), circuitBreaker, this.commandGroup, this.commandKey, this.properties, this.metrics); //初始化断路器
this.threadPool = initThreadPool(threadPool, this.threadPoolKey, threadPoolPropertiesDefaults); //初始化线程池 //Strategies from plugins
this.eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
this.concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
HystrixMetricsPublisherFactory.createOrRetrievePublisherForCommand(this.commandKey, this.commandGroup, this.metrics, this.circuitBreaker, this.properties);
this.executionHook = initExecutionHook(executionHook); this.requestCache = HystrixRequestCache.getInstance(this.commandKey, this.concurrencyStrategy);
this.currentRequestLog = initRequestLog(this.properties.requestLogEnabled().get(), this.concurrencyStrategy); /* fallback semaphore override if applicable */
this.fallbackSemaphoreOverride = fallbackSemaphore; /* execution semaphore override if applicable */
this.executionSemaphoreOverride = executionSemaphore;
}

接下来主要看是如何初始化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() 方法内部

/*
* 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在运行时动态覆盖
*/
private static HystrixThreadPoolKey initThreadPoolKey(HystrixThreadPoolKey threadPoolKey, HystrixCommandGroupKey groupKey, String threadPoolKeyOverride) {
if (threadPoolKeyOverride == null) {
// we don't have a property overriding the value so use either HystrixThreadPoolKey or HystrixCommandGroup
if (threadPoolKey == null) {
/*
* use HystrixCommandGroup if HystrixThreadPoolKey is null
* 如果HystrixThreadPoolKey为空,使用groupKey作为threadPoolKey
*/
return HystrixThreadPoolKey.Factory.asKey(groupKey.name());
} else {
return threadPoolKey;
}
} else {
// we have a property defining the thread-pool so use it instead
return HystrixThreadPoolKey.Factory.asKey(threadPoolKeyOverride);
}
}

可见,在最开始构造HystrixCommand时,threadPoolKeyOverride为null,且没有自己指定的threadPoolKey,也没有默认的threadPoolKey,那么将使用groupKey作为threadPoolKey

所以,默认使用groupKey作为threadPoolKey,而group默认值是标注了@HystrixCommand的类名

最后,看一下如何根据threadPoolKey,初始化threadPool

initThreadPool()

//----------AbstractCommand#initThreadPool()
private static HystrixThreadPool initThreadPool(HystrixThreadPool fromConstructor, HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults) {
// fromConstructor为null,使用HystrixThreadPool.Factory创建线程池
if (fromConstructor == null) {
// get the default implementation of HystrixThreadPool
return HystrixThreadPool.Factory.getInstance(threadPoolKey, threadPoolPropertiesDefaults);
} else {
return fromConstructor;
}
} //----------HystrixThreadPool.Factory#getInstance() 获取HystrixThreadPool实例
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 work
String key = threadPoolKey.name(); // this should find it for all but the first time
// 从缓存threadPools中获取HystrixThreadPool,有则直接返回
HystrixThreadPool previouslyCached = threadPools.get(key);
if (previouslyCached != null) {
return previouslyCached;
} // if we get here this is the first time so we need to initialize
// 第一次初始化HystrixThreadPool
synchronized (HystrixThreadPool.class) {
if (!threadPools.containsKey(key)) {
threadPools.put(key, new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder));
}
}
return threadPools.get(key);
}

先根据threadPoolKey尝试从threadPools这个ConcurrentHashMap<String, HystrixThreadPool>中获取,即从线程池缓存中获取,有就直接返回previouslyCached之前的缓存,如果没有,synchromized对HystrixThreadPool类上锁后,再次判断还是没有threadPoolKey的缓存,就 new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder)

//----------new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder)
public HystrixThreadPoolDefault(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties.Setter propertiesDefaults) {
this.properties = HystrixPropertiesFactory.getThreadPoolProperties(threadPoolKey, propertiesDefaults); //threadPoolProperties
HystrixConcurrencyStrategy 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);
} //----------HystrixConcurrencyStrategy#getThreadPool(HystrixThreadPoolKey, HystrixThreadPoolProperties)
// concurrencyStrategy.getThreadPool()时会创建ThreadPoolExecutor
public 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(); //动态coreSize
final int keepAliveTime = threadPoolProperties.keepAliveTimeMinutes().get(); //大于coreSize的线程,未使用的保活时间
final int maxQueueSize = threadPoolProperties.maxQueueSize().get(); //线程队列最大值
final BlockingQueue<Runnable> workQueue = getBlockingQueue(maxQueueSize); //允许使用maximumSize
if (allowMaximumSizeToDivergeFromCoreSize) {
final int dynamicMaximumSize = threadPoolProperties.maximumSize().get(); //dynamicCoreSize > dynamicMaximumSize,打印error
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);
}
//dynamicCoreSize <= dynamicMaximumSize,正常
else {
return new ThreadPoolExecutor(dynamicCoreSize, dynamicMaximumSize, keepAliveTime, TimeUnit.MINUTES, workQueue, threadFactory);
}
}
else { //不允许使用maximumSize
return new ThreadPoolExecutor(dynamicCoreSize, dynamicCoreSize, keepAliveTime, TimeUnit.MINUTES, workQueue, threadFactory);
}
}

至此,线程池创建完毕

结论

  • 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的机制

【SpringCloud】HystrixCommand的threadPoolKey默认值及线程池初始化的更多相关文章

  1. Java默认提供的线程池

    Java的线程池都是通过ThreadPoolExecutor来构建. public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, ...

  2. c#为字段设置默认值,以及构造函数初始化List对象。

    1.为字段设置默认值 /// <summary> /// 默认值 /// </summary> ; ; /// <summary> /// 页的大小 /// < ...

  3. 了解 .NET 的默认 TaskScheduler 和线程池(ThreadPool)设置,避免让 Task.Run 的性能急剧降低

    .NET Framework 4.5 开始引入 Task.Run,它可以很方便的帮助我们使用 async / await 语法,同时还使用线程池来帮助我们管理线程.以至于我们编写异步代码可以像编写同步 ...

  4. SpringCloud 微服务中 @Async 注解自定义线程池 引发的aop 问题

    背景 在 使用springCloud 的@Async注解来做异步操作时,想自定义其线程池. 引发问题 自定义完线程池后,发现代码里并没有使用自定义线程池里的线程,于是新建一个demo工程,一样的配置代 ...

  5. 线程池 队列 synchronized

    线程池 BlockingQueue synchronized volatile 本章从线程池到阻塞队列BlockingQueue.从BlockingQueue到synchronized 和 volat ...

  6. 从线程池到synchronized关键字详解

    线程池 BlockingQueue synchronized volatile 前段时间看了一篇关于"一名3年工作经验的程序员应该具备的技能"文章,倍受打击.很多熟悉而又陌生的知识 ...

  7. Celery-4.1 用户指南: Configuration and defaults (配置和默认值)

    这篇文档描述了可用的配置选项. 如果你使用默认的加载器,你必须创建 celeryconfig.py 模块并且保证它在python路径中. 配置文件示例 以下是配置示例,你可以从这个开始.它包括运行一个 ...

  8. Java中boolean型变量的默认值问题

    1.首先分析Java中的三种不同变量的区别,如下表所示   概念 默认值 其他 类变量 也叫静态变量,是类中独立于方法之外的变量 用static 修饰 有默认初始值,系统自动初始化. 如boolean ...

  9. 如何给DropDownListFor设置默认值

    1.直接赋值: @Html.DropDownListFor(o => o.ParentId, ViewBag.root as IEnumerable<SelectListItem>, ...

随机推荐

  1. C#中IPAddress转换成整型int

    string addr = "11.22.33.44"; System.Net.IPAddress IPAddr=System.Net.IPAddress.Parse(addr); ...

  2. 再回首数据结构—数组(Golang实现)

    数组为线性数据结构,通常编程语言都有自带了数组数据类型结构,数组存放的是有个相同数据类型的数据集: 为什么称数组为线性数据结构:因为数组在内存中是连续存储的数据结构,数组中每个元素最多只有左右两个方向 ...

  3. hibernate框架中注意的几个问题

    使用hibernate框架中,session.createSQLQuery创建sql语句的时候遇到的问题 1.  select e.id,d.id from emp e,dept d where e. ...

  4. 高精度乘法-17南宁区域赛F -The Chosen One

    题目大意:给你一个n,然后从1~n隔一个选一个,挑出一个集合然后从集合中继续隔一个挑一个,直到只有一个数,问最后一个数是多少?2<=n<=1050 例如n=5,先选出2,4最后选择4.n= ...

  5. (BUG记录)记一次与其他系统交互协作时造成的锁表问题

    最近两日做公司电信某计费项目时,接收一个银行对账的任务,在完成对账后.电信和银行两方金额一致时需要进行充值.冲正操作保持金额一致.冲正服务是JAVA统一调用Tuxedo服务,这个服务已经是一个稳定可用 ...

  6. js拼接字符串后swiper不能动的解决方案

    swiper的配置一定要放在拼接字符串之后,紧随其后,如果放在其他的位置,swiper是不识别HTML的.

  7. 面试作业之浅析京东促销活动核心模型 - DDD

    前言 京东作为中国最大的自营式B2C电商平台,提供一站式综合性购物,服务亿万家庭,涵盖3C.家电.消费品.服饰.家居家装.生鲜和新通路(B2B),满足了消费者的多元化需求.每天都会发布相关的促销活动, ...

  8. 一个月薪两万的Web安全工程师要掌握哪些技能?

    作为一个薪水两万起步的工作,我想知道这些牛人们都会哪些技能呢? Web安全相关概念.熟悉渗透相关工具.渗透实战操作.关注安全圈动态.熟悉Windows/Kali Linux.服务器安全配置.脚本编程学 ...

  9. Javascript高级编程学习笔记(89)—— Canvas(6) 变换

    变换 通过上下文的变化,可以对图像进行处理后再将其绘制到画布上 当我们创建上下文时,会以默认值初始化变化矩阵,在默认的变换矩阵下所有处理都按描述直接绘制. 而当我们为上下文应用变换时,会导致使用不同的 ...

  10. C 基于数组存储的堆栈实现

    一.堆栈简介 对于需要管理的队列,主要操作是在序列的末尾插入和取出(删除)元素,有这样操作要求的序列我们称之为堆栈(Stack). 堆栈可以认为是具有一定约束的线性表,插入和删除都作用在一个称为栈顶( ...