All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.hystrix.metric.consumer.RollingDistributionStream Maven / Gradle / Ivy

There is a newer version: 1.5.18
Show newest version
/**
 * Copyright 2015 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.metric.CachedValuesHistogram; import com.netflix.hystrix.metric.HystrixEvent; import com.netflix.hystrix.metric.HystrixEventStream; import org.HdrHistogram.Histogram; 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 distributions for a given Command. * There is a rolling window abstraction on this stream. * The latency distribution object is calculated over a window of t1 milliseconds. This window has b buckets. * Therefore, a new set of counters is produced every t2 (=t1/b) milliseconds * t1 = metricsRollingPercentileWindowInMilliseconds() * b = metricsRollingPercentileBucketSize() * * These values are stable - there's no peeking into a bucket until it is emitted * * These values get produced and cached in this class. */ public class RollingDistributionStream { private AtomicReference rollingDistributionSubscription = new AtomicReference(null); private final BehaviorSubject rollingDistribution = BehaviorSubject.create(CachedValuesHistogram.backedBy(CachedValuesHistogram.getNewHistogram())); private final Observable rollingDistributionStream; private static final Func2 distributionAggregator = new Func2() { @Override public Histogram call(Histogram initialDistribution, Histogram distributionToAdd) { initialDistribution.add(distributionToAdd); return initialDistribution; } }; private static final Func1, Observable> reduceWindowToSingleDistribution = new Func1, Observable>() { @Override public Observable call(Observable window) { return window.reduce(distributionAggregator); } }; private static final Func1 cacheHistogramValues = new Func1() { @Override public CachedValuesHistogram call(Histogram histogram) { return CachedValuesHistogram.backedBy(histogram); } }; private static final Func1, Observable>> convertToList = new Func1, Observable>>() { @Override public Observable> call(Observable windowOf2) { return windowOf2.toList(); } }; protected RollingDistributionStream(final HystrixEventStream stream, final int numBuckets, final int bucketSizeInMs, final Func2 addValuesToBucket) { final List emptyDistributionsToStart = new ArrayList(); for (int i = 0; i < numBuckets; i++) { emptyDistributionsToStart.add(CachedValuesHistogram.getNewHistogram()); } final Func1, Observable> reduceBucketToSingleDistribution = new Func1, Observable>() { @Override public Observable call(Observable bucket) { return bucket.reduce(CachedValuesHistogram.getNewHistogram(), addValuesToBucket); } }; rollingDistributionStream = stream .observe() .window(bucketSizeInMs, TimeUnit.MILLISECONDS) //stream of unaggregated buckets .flatMap(reduceBucketToSingleDistribution) //stream of aggregated Histograms .startWith(emptyDistributionsToStart) //stream of aggregated Histograms that starts with n empty .window(numBuckets, 1) //windowed stream: each OnNext is a stream of n Histograms .flatMap(reduceWindowToSingleDistribution) //reduced stream: each OnNext is a single Histogram .map(cacheHistogramValues) //convert to CachedValueHistogram (commonly-accessed values are cached) .share() .onBackpressureDrop(); } public Observable observe() { return rollingDistributionStream; } public int getLatestMean() { CachedValuesHistogram latest = getLatest(); if (latest != null) { return latest.getMean(); } else { return 0; } } public int getLatestPercentile(double percentile) { CachedValuesHistogram latest = getLatest(); if (latest != null) { return latest.getValueAtPercentile(percentile); } else { return 0; } } public void startCachingStreamValuesIfUnstarted() { if (rollingDistributionSubscription.get() == null) { //the stream is not yet started Subscription candidateSubscription = observe().subscribe(rollingDistribution); if (rollingDistributionSubscription.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(); } } } CachedValuesHistogram getLatest() { startCachingStreamValuesIfUnstarted(); if (rollingDistribution.hasValue()) { return rollingDistribution.getValue(); } else { return null; } } public void unsubscribe() { Subscription s = rollingDistributionSubscription.get(); if (s != null) { s.unsubscribe(); rollingDistributionSubscription.compareAndSet(s, null); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy