目录
8. 断路器功能
8.1 定义
从类图来看,断路器主要有三个方法,而默认的实现类多一个方法,我们看下这四个方法的作用
allowRequest
: 判断断路器是否放行
isOpen()
: 判断断路器是否开启
allowSingleTest()
: 是否可以半打开,即在断路器打开的状态下允许放行单次请求
markSuccess()
: 半打开状态->关闭
接下来通过源码继续分析
// HystrixCircuitBreakerImpl
public void markSuccess() {
// 判断断路器是否开启,即半打开
if (circuitOpen.get()) {
// 如果开启则将其关闭
if (circuitOpen.compareAndSet(true, false)) {
// 重置统计信息
metrics.resetStream();
}
}
}
@Override
public boolean allowRequest() {
if (properties.circuitBreakerForceOpen().get()) {
// 断路器设置了强制打开,直接返回false
return false;
}
if (properties.circuitBreakerForceClosed().get()) {
// 断路器设置了强制关闭, 但依旧进行信息统计
isOpen();
return true;
}
// 1) 断路器关闭,直接放行
// 2) 断路器开启,查看是否达到半打开的条件,达到则放行本次请求;不达到则拦截
return !isOpen() || allowSingleTest();
}
public boolean allowSingleTest() {
long timeCircuitOpenedOrWasLastTested = circuitOpenedOrLastTestedTime.get();
// 1) 如果断路器开启
// 2) 并且开启时间已经超过设置的休眠时间(circuitBreaker.sleepWindowInMilliseconds)
if (circuitOpen.get() && System.currentTimeMillis() > timeCircuitOpenedOrWasLastTested + properties.circuitBreakerSleepWindowInMilliseconds().get()) {
// 设置当前时间为断路器开启时间
if (circuitOpenedOrLastTestedTime.compareAndSet(timeCircuitOpenedOrWasLastTested, System.currentTimeMillis())) {
// 半打开状态,允许本次请求通行
return true;
}
}
return false;
}
@Override
public boolean isOpen() {
if (circuitOpen.get()) {
return true;
}
// 统计信息metrics请看第10节
HealthCounts health = metrics.getHealthCounts();
// 判断时间窗口内的请求数是否小于circuitBreaker.requestVolumeThreshold(可通过@HystrixProperty设置)
if (health.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) {
// 小于则返回false,断路器不开启
return false;
}
// 判断失败比例是否小于参数circuitBreaker.errorThresholdPercentage(可通过@HystrixProperty设置)
if (health.getErrorPercentage() < properties.circuitBreakerErrorThresholdPercentage().get()) {
// 小于则表明当前请求正常,断路器关闭
return false;
} else {
// 滚动窗口内失败比例过高,则将断路器打开
if (circuitOpen.compareAndSet(false, true)) {
// 设置断路器打开时间
circuitOpenedOrLastTestedTime.set(System.currentTimeMillis());
return true;
} else {
// 多线程情况,另一线程已经做了处理
return true;
}
}
}
8.2 参数设置
关于断路器的参数
@HystrixCommand(commandProperties = {
// 是否开启断路器功能,默认true
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
// 时间窗中打开断路器的最小请求数,默认为10秒内20,也就是,如果10秒内有19次失败也不会打开断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
// 断路器打开多长时间后转为半打开状态,默认5000毫秒,即5秒
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000"),
// 失败百分比,时间窗内失败比例超过此值将会打开断路器,默认50
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
// 是否强制打开断路器,默认false
@HystrixProperty(name = "circuitBreaker.forceOpen", value = "false"),
// 是否强制关闭断路器,默认false
@HystrixProperty(name = "circuitBreaker.forceClosed", value = "false")}
)
public void circuitBreaker() {
}
8.3 初始化
断路器是在抽象类AbstractCommand
使用,因此我们看下其初始化过程
abstract class AbstractCommand<R> implements HystrixInvokableInfo<R>, HystrixObservable<R> {
protected final HystrixCircuitBreaker circuitBreaker;
protected AbstractCommand(......) {
......
this.threadPool = initThreadPool(threadPool, this.threadPoolKey, threadPoolPropertiesDefaults);
......
}
private static HystrixCircuitBreaker initCircuitBreaker(......) {
// "circuitBreaker.enabled", 默认为true,可通过@HystrixProperty设置
if (enabled) {
if (fromConstructor == null) {
// 如果缓存不存在则根据设置的properties新建一个,单例模式
return HystrixCircuitBreaker.Factory.getInstance(commandKey, groupKey, properties, metrics);
} else {
return fromConstructor;
}
} else {
return new NoOpCircuitBreaker();
}
}
}
9. 隔离与限流
Hystrix有两种隔离策略:SEMAPHORE和THREAD。
9.1 SEMAPHORE策略
9.1.1 参数设置
@HystrixCommand(commandProperties = {
// THREAD,SEMAPHORE
@HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"),
// 最大并发量
@HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "30")}
)
public void test() {
}
9.1.2 获取信号源
// AbstractCommand
protected TryableSemaphore getExecutionSemaphore() {
// 判断隔离策略
if (properties.executionIsolationStrategy().get() == ExecutionIsolationStrategy.SEMAPHORE) {
// 隔离策略为SEMAPHORE
if (executionSemaphoreOverride == null) {
TryableSemaphore _s = executionSemaphorePerCircuit.get(commandKey.name());
if (_s == null) {
// 缓存中没有则新建,信号量大小为execution.isolation.semaphore.maxConcurrentRequests的值
// 信号源池executionSemaphorePerCircuit,key为commandKey,值为信号源TryableSemaphore
// 通过不同commandKey将请求进行隔离
executionSemaphorePerCircuit.putIfAbsent(commandKey.name(), new TryableSemaphoreActual(properties.executionIsolationSemaphoreMaxConcurrentRequests()));
return executionSemaphorePerCircuit.get(commandKey.name());
} else {
return _s;
}
} else {
return executionSemaphoreOverride;
}
} else {
// 隔离策略为THREAD,返回一个永远可以得到许可的信号源
return TryableSemaphoreNoOp.DEFAULT;
}
}
9.1.3 使用信号量
// AbstractCommand
private Observable<R> applyHystrixSemantics(final AbstractCommand<R> _cmd) {
......
if (circuitBreaker.allowRequest()) {
// (1)获取信号源
final TryableSemaphore executionSemaphore = getExecutionSemaphore();
......
// 获取许可,进行限流处理
if (executionSemaphore.tryAcquire()) {
// 获取成功,执行正常逻辑
executeCommandAndObserve(......)
} else {
// 被限流,调用Fallback降级处理
return handleSemaphoreRejectionViaFallback();
}
} else {
return handleShortCircuitViaFallback();
}
}
9.2 THREAD策略
// AbstractCommand
private Observable<R> executeCommandWithSpecifiedIsolation(final AbstractCommand<R> _cmd) {
if (properties.executionIsolationStrategy().get() == ExecutionIsolationStrategy.THREAD) {
return Observable.defer(new Func0<Observable<R>>() {
// 2) 获得线程资源后调用call方法
@Override
public Observable<R> call() {
......
}
}).doOnTerminate(new Action0() {
......
}).doOnUnsubscribe(new Action0() {
......
}).subscribeOn(
threadPool.getScheduler(......) // 1) 初始化Scheduler
);
} else {
......
}
}
简单理解就是,
-
通过Observable.defer(…)生成一个Observable观察对象
-
当该对象被订阅的时候,1)处的调度器将2)处的
call()
放到线程池中 -
当获得线程资源后开始执行2)处的
call()
而1)调度器是由线程池
threadPool
创建出来的。那么这里我们就要分析,第一,threadPool
是怎么来的;第二,它的工作原理
9.2.1 threadPool
线程池参数设置
@HystrixCommand(
// 定义threadPoolKey
threadPoolKey = "myThreadPoolKey",
// 参考HystrixCommandProperties类
commandProperties = {
// 设置隔离策略为THREAD, 默认为THREAD
@HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
// 超时时间, 默认为1000毫秒
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000"),
// 超时是否中断执行线程,默认true
@HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"),
// 取消Future/Thread是否中断执行线程,默认false
@HystrixProperty(name = "execution.isolation.thread.interruptOnFutureCancel", value = "false")
},
// 线程池具体参数可参考HystrixThreadPoolProperties类
threadPoolProperties = {
// 线程核心数
@HystrixProperty(name = "coreSize", value = "10"),
// 线程最大数
@HystrixProperty(name = "maximumSize", value = "10"),
// 线程空闲存活时间
@HystrixProperty(name = "keepAliveTimeMinutes", value = "1"),
// 任务队列的长度,-1则使用SynchronousQueue
// 这个值不能动态修改
@HystrixProperty(name = "maxQueueSize", value = "-1"),
// 任务队列阈值,超过该数值则拒绝任务入列
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "5"),
// maximumSize是否可配置
@HystrixProperty(name = "allowMaximumSizeToDivergeFromCoreSize", value = "false"),
// 线程池度量时间窗口长度,默认10秒
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "10000"),
// 线程池度量时间窗口内的桶数量,默认为10,也就是1秒一个桶
@HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10")
})
public void thread() {
}
初始化
// AbstractCommand
protected AbstractCommand(......) {
......
this.threadPool = initThreadPool(threadPool, this.threadPoolKey, threadPoolPropertiesDefaults);
......
}
private static HystrixThreadPool initThreadPool(......) {
if (fromConstructor == null) {
return HystrixThreadPool.Factory.getInstance(threadPoolKey, threadPoolPropertiesDefaults);
} else {
return fromConstructor;
}
}
HystrixThreadPool.Factory.getInstance
// HystrixCircuitBreaker.Factory
// HystrixThreadPool采用单例模式,内部维护一个ConcurrentHashMap的threadPools,key为threadPoolKey,值为对应的线程池
static HystrixThreadPool getInstance(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties.Setter propertiesBuilder) {
String key = threadPoolKey.name();
// 先从缓存获取
HystrixThreadPool previouslyCached = threadPools.get(key);
if (previouslyCached != null) {
return previouslyCached;
}
// 缓存不存在则新建
// threadPools为线程池组,key为threadPoolKey,值为具体的线程池
// 将请求按不同的threadPoolKey分组,进行隔离
synchronized (HystrixThreadPool.class) {
if (!threadPools.containsKey(key)) {
threadPools.put(key, new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder));
}
}
return threadPools.get(key);
}
线程池的默认类HystrixThreadPoolDefault
// 这里最重要的其实是propertiesDefaults,这个其实是从参数设置里获得的,也就是threadPoolProperties
public HystrixThreadPoolDefault(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties.Setter propertiesDefaults) {
this.properties = HystrixPropertiesFactory.getThreadPoolProperties(threadPoolKey, propertiesDefaults);
HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
this.queueSize = properties.maxQueueSize().get();
this.metrics = HystrixThreadPoolMetrics.getInstance(threadPoolKey,
concurrencyStrategy.getThreadPool(threadPoolKey, properties),
properties);
this.threadPool = this.metrics.getThreadPool();
this.queue = this.threadPool.getQueue();
HystrixMetricsPublisherFactory.createOrRetrievePublisherForThreadPool(threadPoolKey, this.metrics, this.properties);
}
至此线程池threadPool
就创建好了,那么接下来分析threadPool.getScheduler(......)
方法
9.2.2 线程池策略的工作原理
内部代码比较繁琐,所以这里用一个流程图描述大概过程,然后重点分析关键处的代码
从图中我们看到最重要的部分其实就是绿色和红色的部分,红色可以理解为任务,绿色为提交任务到线程池,接下来我们就看下这两部分的代码
红色:具体任务
AbstractCommand.executeCommandWithSpecifiedIsolation()
// AbstractCommand
private Observable<R> executeCommandWithSpecifiedIsolation(final AbstractCommand<R> _cmd) {
if (properties.executionIsolationStrategy().get() == ExecutionIsolationStrategy.THREAD) {
return Observable.defer(new Func0<Observable<R>>() {
@Override
public Observable<R> call() {
......
// 统计信息
metrics.markCommandStart(commandKey, threadPoolKey, ExecutionIsolationStrategy.THREAD);
......
// 标记线程池开始执行任务
threadPool.markThreadExecution();
......
// 执行具体的任务,最终会利用反射执行原始方法,后面不在具体分析
return getUserExecutionObservable(_cmd);
......
}
})......;
} else {
......
}
}
绿色:将任务提交到线程池
ThreadPoolWorker.schedule()
// ThreadPoolWorker
@Override
public Subscription schedule(final Action0 action) {
if (subscription.isUnsubscribed()) {
return Subscriptions.unsubscribed();
}
// 将红色部分包装成Runnable
ScheduledAction sa = new ScheduledAction(action);
subscription.add(sa);
sa.addParent(subscription);
// 获取线程池
ThreadPoolExecutor executor = (ThreadPoolExecutor) threadPool.getExecutor();
// 将Runnable提交到线程池,线程根据自身的线程数大小以及任务队列大小进行一定程度的限流
FutureTask<?> f = (FutureTask<?>) executor.submit(sa);
sa.add(new FutureCompleterWithConfigurableInterrupt(f, shouldInterruptThread, executor));
return sa;
}
未完待续