HystrixCircuitBreakerImpl熔断器之滑动窗口

       这里主要是介绍熔断器在关闭状态下内部是如何不断的检查命令的执行结果来对应更改状态,其实就是利用Rxjava实现了一个滑动窗口,下面就在源码中看看是怎么回事。

1、先还是再整体看看HystrixCircuitBreakerImpl

class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker {
	private final HystrixCommandProperties properties; //配置:包括了滑动窗口的设置
	private final HystrixCommandMetrics metrics; //命令执行记录的数据流

	enum Status {
		CLOSED, OPEN, HALF_OPEN; //熔断器状态,分别对应:关闭、打开、半开
	}

	private final AtomicReference<Status> status = new AtomicReference<Status>(Status.CLOSED);
	private final AtomicLong circuitOpened = new AtomicLong(-1);
	private final AtomicReference<Subscription> activeSubscription = new AtomicReference<Subscription>(null);

	protected HystrixCircuitBreakerImpl(HystrixCommandKey key, HystrixCommandGroupKey commandGroup, final HystrixCommandProperties properties, HystrixCommandMetrics metrics) {
		this.properties = properties;
		this.metrics = metrics;

		//On a timer, this will set the circuit between OPEN/CLOSED as command executions occur
		Subscription s = subscribeToStream();
		activeSubscription.set(s);
	}

	private Subscription subscribeToStream() {
		/*
		 * This stream will recalculate the OPEN/CLOSED status on every onNext from the health stream
		 */
		return metrics.getHealthCountsStream()
				.observe() // 滑动窗口在里面,滑动窗口在里面,滑动窗口在里面!!!
				.subscribe(new Subscriber<HealthCounts>() {
					@Override
					public void onCompleted() {

					}

					@Override
					public void onError(Throwable e) {

					}

					@Override
					public void onNext(HealthCounts hc) { // 根据滑动窗口里的结果更改熔断器状态
						// check if we are past the statisticalWindowVolumeThreshold
						if (hc.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) {
							// we are not past the minimum volume threshold for the stat window,
							// so no change to circuit status.
							// if it was CLOSED, it stays CLOSED
							// if it was half-open, we need to wait for a successful command execution
							// if it was open, we need to wait for sleep window to elapse
						} else {
							if (hc.getErrorPercentage() < properties.circuitBreakerErrorThresholdPercentage().get()) {
								//we are not past the minimum error threshold for the stat window,
								// so no change to circuit status.
								// if it was CLOSED, it stays CLOSED
								// if it was half-open, we need to wait for a successful command execution
								// if it was open, we need to wait for sleep window to elapse
							} else {
								// our failure rate is too high, we need to set the state to OPEN
								if (status.compareAndSet(Status.CLOSED, Status.OPEN)) {
									circuitOpened.set(System.currentTimeMillis());
								}
							}
						}
					}
				});
	}

	@Override
	public void markSuccess() { // 这个是根据一个命令的实时执行结果,成功则将熔断器由半开状态变为关闭状态
		if (status.compareAndSet(Status.HALF_OPEN, Status.CLOSED)) {
			//This thread wins the race to close the circuit - it resets the stream to start it over from 0
			metrics.resetStream();
			Subscription previousSubscription = activeSubscription.get();
			if (previousSubscription != null) {
				previousSubscription.unsubscribe();
			}
			Subscription newSubscription = subscribeToStream();
			activeSubscription.set(newSubscription);
			circuitOpened.set(-1L);
		}
	}

	@Override
	public void markNonSuccess() {// 这个是根据一个命令的实时执行结果,失败则将熔断器由半开状态变为打开状态
		if (status.compareAndSet(Status.HALF_OPEN, Status.OPEN)) {
			//This thread wins the race to re-open the circuit - it resets the start time for the sleep window
			circuitOpened.set(System.currentTimeMillis());
		}
	}

	@Override
	public boolean isOpen() {
		if (properties.circuitBreakerForceOpen().get()) {
			return true;
		}
		if (properties.circuitBreakerForceClosed().get()) {
			return false;
		}
		return circuitOpened.get() >= 0;
	}

	@Override
	public boolean allowRequest() {
		if (properties.circuitBreakerForceOpen().get()) {
			return false;
		}
		if (properties.circuitBreakerForceClosed().get()) {
			return true;
		}
		if (circuitOpened.get() == -1) {
			return true;
		} else {
			if (status.get().equals(Status.HALF_OPEN)) {
				return false;
			} else {
				return isAfterSleepWindow();
			}
		}
	}

	private boolean isAfterSleepWindow() { // 判断熔断器是否可进入半开状态
		final long circuitOpenTime = circuitOpened.get();
		final long currentTime = System.currentTimeMillis();
		final long sleepWindowTime = properties.circuitBreakerSleepWindowInMilliseconds().get();
		return currentTime > circuitOpenTime + sleepWindowTime;
	}

	@Override
	public boolean attemptExecution() {
		if (properties.circuitBreakerForceOpen().get()) {
			return false;
		}
		if (properties.circuitBreakerForceClosed().get()) {
			return true;
		}
		if (circuitOpened.get() == -1) {
			return true;
		} else {
			if (isAfterSleepWindow()) {
				if (status.compareAndSet(Status.OPEN, Status.HALF_OPEN)) {
					//only the first request after sleep window should execute
					return true;
				} else {
					return false;
				}
			} else {
				return false;
			}
		}
	}
}

 

        进入observe(),可以看到是返回一个sourceStream,它是一个Observable。在当前类的构造函数中可以看到sourceStream又来源于bucketedStream.window(numBuckets, 1) .......

 this.sourceStream = bucketedStream      //stream broken up into buckets
                .window(numBuckets, 1)          //滑动窗口,滑动窗口,滑动窗口  emit overlapping windows of buckets
                .flatMap(reduceWindowToSummary) //convert a window of bucket-summaries into a single summary
                .doOnSubscribe(new Action0() {
                    @Override
                    public void call() {
                        isSourceCurrentlySubscribed.set(true);
                    }
                })
                .doOnUnsubscribe(new Action0() {
                    @Override
                    public void call() {
                        isSourceCurrentlySubscribed.set(false);
                    }
                })
                .share()                        //multiple subscribers should get same data
                .onBackpressureDrop();          //if there are slow consumers, data should not buffer

进入bucketedStream所在类的构造函数中可以看到如下代码

this.bucketedStream = Observable.defer(new Func0<Observable<Bucket>>() {
	@Override
	public Observable<Bucket> call() {
		return inputEventStream
				.observe()
				.window(bucketSizeInMs, TimeUnit.MILLISECONDS) //滑动窗口,滑动窗口,滑动窗口 bucket it by the counter window so we can emit to the next operator in time chunks, not on every OnNext
				.flatMap(reduceBucketToSummary)                //for a given bucket, turn it into a long array containing counts of event types
				.startWith(emptyEventCountsToStart);           //start it with empty arrays to make consumer logic as generic as possible (windows are always full)
	}
});

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值