SpringCloud Hystrix源码解析
看这篇之前请看
SpringCloud Hystrix源码解析(一)
了解了HystrixObservable 中的两个关键接口在AbstractCommand 中的实现后, 接下来我们需要到HystrixCommand 中了解execute 同步执行命令和queue 异步执行命令的相关实现。
HystrixCommand#queue
代码如下所示:
public Future <R> queue ( ) {
final Future<R> delegate = toObservable() . toBlocking() . toFuture() ;
final Future<R> f =new Future<R>() {
return f ;
}
queu e 方法中将A bst ractCommand#toO bs ervab l e 获取到的Observable 通过toBlocking转化为具备阻塞功能的BlockingObservable ,再通过toFuture 方法获取到能够执行run 抽象方法的Future , 最后通过Future 得到正在异步执行的命令的执行结果。
HystrixCommand#execute
代码如下所示:
public R execute() {
try {
return queue().get() ;
} catch (Exception e) {
throw Exceptions.sneakyThrow(decomposeException(e)) ;
}
exeute 方法通过queue 获取到Future ,使用Future#get 方法获取到命令的执行结果,它将一直阻塞线程直到有执行结果返回。
HystrixCircuitBreaker 是Hystrix 提供断路器逻辑的核心接口,它通过Hystri xCommandKey(由@ HystrixCommand 的commandKey 构造而成)与每一个Hys tri xComman d 绑定。在HystrixCircuitBreaker. Factory 中使用Conc urrentHashMap 维持了基于HystrixCommandKey的HystrixCircuitBreaker 的单例映射表,保证具备相同CommandKey 的HystrixCommand对应同一个断路器。
HystrixCircuitBreaker 有两个默认实现,一个是NoOpCircuitBreaker ,顾名思义即空实现,不会发挥任何断路器的功能,另一个实现HystrixCircuitBreakerlmpl ,为断路器的真正实现。
断路器强制开始和关闭的相关配置可以通过配置中心的方式动态修改,这样就可以人为干预断路器的状态,方便调试。断路器打开的时候将会记录一个打开时间,用于判断断路器是否打开,通过它与配置中的circuitBreakerSle巳pWindow InMillis e conds 重置时间结合
判断:在断路器打开一段时间后(重置时间结束) ,允许尝试执行命令, 检查远程调用是否恢复到可使用的状态。
HystrixCommandMetrics 统计命令执行情况
断路器通过向HystrixCommandMetrics 中的请求执行统Observable 发起订阅来完成断路器自动打开的相关逻辑。
HystrixCommandMetrics 统计了同- HystrixCommand 请求的指标数据,包括链路健康统计流HealthCountsStream 。HealthCountsStream 中使用滑动窗口的方式对各项数据( HealthCounts )进行统计,在一个滑动窗口时间中又划分了若干个bucket (滑动窗口时间与bucket 成整数倍关系),滑动窗口的移动是以bucket 为单位,每个bucket 仅统计该时间间隔内的请求数据。最后按照滑动窗口的大小对每个bucket 中的统计数据进行聚合,得到周期时间内的统计数据Health Counts 。以下是Health Counts 中统计的数据项:
Hystrix 使用rx 中的Observable#w indow 实现滑动窗口,通过rx 中单线程的无锁特性保证计数变更时的线程安全,后台线程创建新bucket ,避免并发情况。
下面是HealthCountsStream 的父类创建滑动窗口的相关代码:
上述代码中,#window 定义了每发射一次数据( 此时’一个数据项将会被从滑动窗口中移除,以及创建一个新的bucket 用于统计)都会聚合numBuckets 个数据项,即整个滑动窗口的数据统计集合AbstractCommand 在命令执行结束后的回调方法中,通过HystrixC ommandMetrics 统计相关命令的执行结果,这其中主要通过HystrixCommandCompletion 数据类对命令执行结束后的事件流进行统计,其中的事件类型由HystrixEventType 定义。
HystrixCommandCompletion 由HystrixCommandCompletionStream 进行管理,最终在HealthCountsStream 中用于统计一段时间内的链路健康情况。
在AbstractCommand#applyHystrixSemantics 方法中,如果发现断路器关闭,将会尝试获取信号量。在Hystrix 中,主要有两种策略进行资源隔离, 一种是信号量隔离的策略, 另一种是线程隔离的策略, 下面将对这两种资源隔离策略进行介绍
信号量隔离策略
信号量隔离主要由TryableSemaphore 接口提供, 如下所示:
//TryableSemaphore java
interface TryableSemaphore {
//尝试获取信号量
public abstract boolean tryAcquire () ;
//释放信号量
public abstract void release() ;
//获取已被使用信号量数量
public abstractInt getNumberOfPermitsUsed() ;
}
线程隔离策略
在AbstractCommand #execut巳CommandWithSpecifiedlsolation 的方法中,线程隔离策略与信号量隔离策略的主要区别是,线程隔离策略将Obserable 的执行线程通过HystrixThreadPool#getScheduler 方法进行了指定。
HystrixThreadPool 的作用是将HystrixCommand# run 方法指定到隔离的线程中执行。HystrixThreadPool 是由HystrixThreadPool.Factory 生成和管理的,通过ThreadPoolKey(由@ HystrixCommand 中thre adPoolKey 指定)与HystrixCommand 进行绑定,它的默认实现为HystrixThreadPoolDefault HystrixThreadPoolDefault 中的线程池ThreadPoolExecutor 通过HystrixConcurrencyStrategy 策略生成
通过线程隔离的方式, 可以将调用线程与执行命令的线程分隔开来,避免了调用线程被阻塞。同时通过线程池的方式对每种命令的并发线程数量进行控制,避免了一种命令的阻塞影响系统的其他请求的执行,很好地保护了服务调用者的线程资源。
Hystrix 中执行失败回滚的逻辑主要封装AbstractCornrnand#executeCornrnandAndObserve
#handleFallback 的异常回调方法中,根据执行过程中抛出的异常用不同的方法对其进行处理,返回带有失败回滚逻辑的Observable 。
在handleFallback 方法中对不同的执行错误调用不同的处理方法,主要有:
除此之外,在applyHystrixSemantics 中包括了断路失败处理方法和获取信号量失败处理方法: