熔断器 Hystrix 源码解析 —— 命令执行(二)之执行隔离策略

  1. 概述

本文主要分享 Hystrix 命令执行(二)之执行隔离策略。

建议 :对 RxJava 已经有一定的了解的基础上阅读本文。

Hystrix 提供两种执行隔离策略( ExecutionIsolationStrategy ) :

SEMAPHORE :信号量,命令在调用线程执行。在《Hystrix 源码解析 —— 命令执行(一)之正常执行逻辑》「3. TryableSemaphore」 已经详细解析。

THREAD :线程池,命令在线程池执行。在《Hystrix 源码解析 —— 命令执行(一)之正常执行逻辑》「5. #executeCommandWithSpecifiedIsolation(…)」 的 #executeCommandWithSpecifiedIsolation(…) 方法中,调用 Observable#subscribeOn(Scheduler) 方法,指定在 RxJava Scheduler 执行。

如果你暂时不了解 Scheduler ,可以阅读 《RxJava 源码解析 —— Scheduler》 。

如果你暂时不了解 Observable#subscribeOn(Scheduler) ,可以阅读 《RxJava 源码解析 —— Observable#subscribeOn(Scheduler)》 。

两种方式的优缺点比较,推荐阅读 《【翻译】Hystrix文档-实现原理》「依赖隔离」。

推荐 Spring Cloud 书籍:

请支持正版。下载盗版,等于主动编写低级 BUG 。

程序猿DD —— 《Spring Cloud微服务实战》

周立 —— 《Spring Cloud与Docker微服务架构实战》

两书齐买,京东包邮。

  1. HystrixThreadPoolProperties

com.netflix.hystrix.HystrixThreadPoolProperties ,Hystrix 线程池属性配置抽象类,点击 链接 查看,已添加中文注释说明。

com.netflix.hystrix.strategy.properties.HystrixPropertiesThreadPoolDefault ,Hystrix 线程池配置实现类,点击 链接 查看。实际上没什么内容,官方如是说 :

Default implementation of {@link HystrixThreadPoolProperties} using Archaius (https://github.com/Netflix/archaius)

  1. HystrixThreadPoolKey

com.netflix.hystrix.HystrixThreadPoolKey ,Hystrix 线程池标识接口。

FROM HystrixThreadPoolKey 接口注释
A key to represent a {@link HystrixThreadPool} for monitoring, metrics publishing, caching and other such uses.
This interface is intended to work natively with Enums so that implementing code can be an enum that implements this interface.

直白的说 ,希望通过相同的 name ( 标识 ) 获得同 HystrixThreadPoolKey 对象。通过在内部维持一个 name 与 HystrixThreadPoolKey 对象的映射,以达到枚举的效果。

HystrixThreadPoolKey 代码如下 :

1
:

public

interface

HystrixThreadPoolKey

extends

HystrixKey

{

2
:

class

Factory

{

3
:

private

Factory
()

{

4
:

}

5
:

6
:

// used to intern instances so we don’t keep re-creating them millions of times for the same key

7
:

private

static

final

InternMap
<
String
,

HystrixThreadPoolKey

intern

8
:

=

new

InternMap
<
String
,

HystrixThreadPoolKey

(

9
:

new

InternMap
.
ValueConstructor
<
String
,

HystrixThreadPoolKey

()

{

10
:

@Override

11
:

public

HystrixThreadPoolKey
create
(
String
key
)

{

12
:

return

new

HystrixThreadPoolKeyDefault
(
key
);

13
:

}

14
:

});

15
:

16
:

public

static

HystrixThreadPoolKey
asKey
(
String
name
)

{

17
:

return
intern
.
interned
(
name
);

18
:

}

19
:

20
:

private

static

class

HystrixThreadPoolKeyDefault

extends

HystrixKeyDefault

implements

HystrixThreadPoolKey

{

21
:

public

HystrixThreadPoolKeyDefault
(
String
name
)

{

22
:

super
(
name
);

23
:

}

24
:

}

25
:

26
:

/* package-private */

static

int
getThreadPoolCount
()

{

27
:

return
intern
.
size
();

28
:

}

29
:

}

30
:

}

HystrixThreadPoolKey 实现 com.netflix.hystrix.HystrixKey 接口,点击 链接 查看。该接口定义的 #name()方法,即是上文我们所说的标识( Key )。

intern 属性, name 与 HystrixThreadPoolKey 对象的映射,以达到枚举的效果。

com.netflix.hystrix.util.InternMap ,点击 链接 查看带中文注释的代码。

#asKey(name) 方法,从 intern 获得 HystrixThreadPoolKey 对象。

#getThreadPoolCount() 方法,获得 HystrixThreadPoolKey 数量。

在 AbstractCommand 构造方法里,初始化命令的 threadPoolKey 属性,代码如下 :

protected

final

HystrixThreadPoolKey
threadPoolKey
;

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
);

this
.
commandKey

initCommandKey
(
key
,
getClass
());

this
.
properties

initCommandProperties
(
this
.
commandKey
,
propertiesStrategy
,
commandPropertiesDefaults
);

// 初始化 threadPoolKey

this
.
threadPoolKey

initThreadPoolKey
(
threadPoolKey
,

this
.
commandGroup
,

this
.
properties
.
executionIsolationThreadPoolKeyOverride
().
get
());

}

调用 #initThreadPoolKey(…) 方法,创建最终的 threadPoolKey 属性。代码如下 :

优先级 : threadPoolKeyOverride > threadPoolKey > groupKey

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 */

return
HystrixThreadPoolKey
.
Factory
.
asKey
(
groupKey
.
name
());

}
else
{

return
threadPoolKey
;

}

}
else
{
// threadPoolKeyOverride 可覆盖属性

// we have a property defining the thread-pool so use it instead

return
HystrixThreadPoolKey
.
Factory
.
asKey
(
threadPoolKeyOverride
);

}

}

  1. HystrixConcurrencyStrategy

com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy ,Hystrix 并发策略抽象类。

HystrixConcurrencyStrategy#getThreadPool(…) 方法,代码如下 :

1
:

public

ThreadPoolExecutor
getThreadPool
(
final

HystrixThreadPoolKey
threadPoolKey
,

HystrixThreadPoolProperties
threadPoolProperties
)

{

2
:

final

ThreadFactory
threadFactory

getThreadFactory
(
threadPoolKey
);

3
:

4
:

final

boolean
allowMaximumSizeToDivergeFromCoreSize

threadPoolProperties
.
getAllowMaximumSizeToDivergeFromCoreSize
().
get
();

5
:

final

int
dynamicCoreSize

threadPoolProperties
.
coreSize
().
get
();

6
:

final

int
keepAliveTime

threadPoolProperties
.
keepAliveTimeMinutes
().
get
();

7
:

final

int
maxQueueSize

threadPoolProperties
.
maxQueueSize
().
get
();

8
:

9
:

final

BlockingQueue
<
Runnable

workQueue

getBlockingQueue
(
maxQueueSize
);

10
:

11
:

if

(
allowMaximumSizeToDivergeFromCoreSize
)

{

12
:

final

int
dynamicMaximumSize

threadPoolProperties
.
maximumSize
().
get
();

13
:

if

(
dynamicCoreSize

dynamicMaximumSize
)

{

14
:
logger
.
error
(
"Hystrix ThreadPool configuration at startup for : "

threadPoolKey
.
name
()

" is trying to set coreSize = "

15
:
dynamicCoreSize
+

" and maximumSize = "

dynamicMaximumSize
+

". Maximum size will be set to "

16
:
dynamicCoreSize
+

“, the coreSize value, since it must be equal to or greater than the coreSize value”
);

17
:

return

new

ThreadPoolExecutor
(
dynamicCoreSize
,
dynamicCoreSize
,
keepAliveTime
,

TimeUnit
.
MINUTES
,
workQueue
,
threadFactory
);

18
:

}

else

{

19
:

return

new

ThreadPoolExecutor
(
dynamicCoreSize
,
dynamicMaximumSize
,
keepAliveTime
,

TimeUnit
.
MINUTES
,
workQueue
,
threadFactory
);

20
:

}

21
:

}

else

{

22
:

return

new

ThreadPoolExecutor
(
dynamicCoreSize
,
dynamicCoreSize
,
keepAliveTime
,

TimeUnit
.
MINUTES
,
workQueue
,
threadFactory
);

23
:

}

24
:

}

第 2 行 :调用 #getThreadFactory(…) 方法,获得 ThreadFactory 。点击 链接 查看方法代码。

PlatformSpecific#getAppEngineThreadFactory() 方法,无需细看,适用于 Google App Engine 场景。

第 4 至 7 行 :「2. HystrixThreadPoolProperties」 有详细解析。

第 9 行 :调用 #getBlockingQueue() 方法,获得线程池的阻塞队列。点击 链接 查看方法代码。

《Java阻塞队列ArrayBlockingQueue和LinkedBlockingQueue实现原理分析》

《Java并发包中的同步队列SynchronousQueue实现原理》

当 maxQueueSize<=0 时( 默认值 : -1 ) 时,使用 SynchronousQueue 。超过线程池的 maximumPoolSize时,提交任务被拒绝。

当 SynchronousQueue>0 时,使用 LinkedBlockingQueue 。超过线程池的 maximumPoolSize 时,任务被拒绝。超过线程池的 maximumPoolSize + 线程池队列的 maxQueueSize 时,提交任务被阻塞等待。

推荐 :《聊聊并发(三)——JAVA线程池的分析和使用》

推荐 :《聊聊并发(七)——Java中的阻塞队列》

第 11 至 23 行 :创建 ThreadPoolExecutor 。看起来代码比较多,根据 allowMaximumSizeToDivergeFromCoreSize的情况,计算线程池的 maximumPoolSize 属性。计算的方式和 HystrixThreadPoolProperties#actualMaximumSize() 方法是一致的。

猜你喜欢

转载自blog.csdn.net/weixin_44767512/article/details/89184505
今日推荐