com.netflix.hystrix.metric.consumer.RollingConcurrencyStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hystrix-core Show documentation
Show all versions of hystrix-core Show documentation
hystrix-core developed by Netflix
/**
* Copyright 2016 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.hystrix.metric.consumer;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.metric.HystrixCommandExecutionStarted;
import com.netflix.hystrix.metric.HystrixEventStream;
import rx.Observable;
import rx.Subscription;
import rx.functions.Func0;
import rx.functions.Func1;
import rx.functions.Func2;
import rx.subjects.BehaviorSubject;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* Maintains a stream of max-concurrency
*
* This gets calculated using a rolling window of t1 milliseconds. This window has b buckets.
* Therefore, a new rolling-max is produced every t2 (=t1/b) milliseconds
* t1 = {@link HystrixCommandProperties#metricsRollingStatisticalWindowInMilliseconds()}
* b = {@link HystrixCommandProperties#metricsRollingStatisticalWindowBuckets()}
*
* This value gets cached in this class. It may be queried using {@link #getLatestRollingMax()}
*
* This is a stable value - there's no peeking into a bucket until it is emitted
*/
public abstract class RollingConcurrencyStream {
private AtomicReference rollingMaxSubscription = new AtomicReference(null);
private final BehaviorSubject rollingMax = BehaviorSubject.create(0);
private final Observable rollingMaxStream;
private static final Func2 reduceToMax = new Func2() {
@Override
public Integer call(Integer a, Integer b) {
return Math.max(a, b);
}
};
private static final Func1, Observable> reduceStreamToMax = new Func1, Observable>() {
@Override
public Observable call(Observable observedConcurrency) {
return observedConcurrency.reduce(0, reduceToMax);
}
};
private static final Func1 getConcurrencyCountFromEvent = new Func1() {
@Override
public Integer call(HystrixCommandExecutionStarted event) {
return event.getCurrentConcurrency();
}
};
protected RollingConcurrencyStream(final HystrixEventStream inputEventStream, final int numBuckets, final int bucketSizeInMs) {
final List emptyRollingMaxBuckets = new ArrayList();
for (int i = 0; i < numBuckets; i++) {
emptyRollingMaxBuckets.add(0);
}
rollingMaxStream = inputEventStream
.observe()
.map(getConcurrencyCountFromEvent)
.window(bucketSizeInMs, TimeUnit.MILLISECONDS)
.flatMap(reduceStreamToMax)
.startWith(emptyRollingMaxBuckets)
.window(numBuckets, 1)
.flatMap(reduceStreamToMax)
.share()
.onBackpressureDrop();
}
public void startCachingStreamValuesIfUnstarted() {
if (rollingMaxSubscription.get() == null) {
//the stream is not yet started
Subscription candidateSubscription = observe().subscribe(rollingMax);
if (rollingMaxSubscription.compareAndSet(null, candidateSubscription)) {
//won the race to set the subscription
} else {
//lost the race to set the subscription, so we need to cancel this one
candidateSubscription.unsubscribe();
}
}
}
public long getLatestRollingMax() {
startCachingStreamValuesIfUnstarted();
if (rollingMax.hasValue()) {
return rollingMax.getValue();
} else {
return 0L;
}
}
public Observable observe() {
return rollingMaxStream;
}
public void unsubscribe() {
Subscription s = rollingMaxSubscription.get();
if (s != null) {
s.unsubscribe();
rollingMaxSubscription.compareAndSet(s, null);
}
}
}