예제 #1
0
  protected RollingDistributionStream(
      final HystrixEventStream<Event> stream,
      final int numBuckets,
      final int bucketSizeInMs,
      final Func2<Histogram, Event, Histogram> addValuesToBucket) {
    final List<Histogram> emptyDistributionsToStart = new ArrayList<Histogram>();
    for (int i = 0; i < numBuckets; i++) {
      emptyDistributionsToStart.add(CachedValuesHistogram.getNewHistogram());
    }

    final Func1<Observable<Event>, Observable<Histogram>> reduceBucketToSingleDistribution =
        new Func1<Observable<Event>, Observable<Histogram>>() {
          @Override
          public Observable<Histogram> call(Observable<Event> bucket) {
            return bucket.reduce(CachedValuesHistogram.getNewHistogram(), addValuesToBucket);
          }
        };

    rollingDistributionStream =
        Observable.defer(
                new Func0<Observable<CachedValuesHistogram>>() {
                  @Override
                  public Observable<CachedValuesHistogram> call() {
                    return 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(); // multicast
  }
예제 #2
0
/**
 * 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()
 *
 * <p>These values are stable - there's no peeking into a bucket until it is emitted
 *
 * <p>These values get produced and cached in this class.
 */
public class RollingDistributionStream<Event extends HystrixEvent> {
  private AtomicReference<Subscription> rollingDistributionSubscription =
      new AtomicReference<Subscription>(null);
  private final BehaviorSubject<CachedValuesHistogram> rollingDistribution =
      BehaviorSubject.create(
          CachedValuesHistogram.backedBy(CachedValuesHistogram.getNewHistogram()));
  private final Observable<CachedValuesHistogram> rollingDistributionStream;

  private static final Func2<Histogram, Histogram, Histogram> distributionAggregator =
      new Func2<Histogram, Histogram, Histogram>() {
        @Override
        public Histogram call(Histogram initialDistribution, Histogram distributionToAdd) {
          initialDistribution.add(distributionToAdd);
          return initialDistribution;
        }
      };

  private static final Func1<Observable<Histogram>, Observable<Histogram>>
      reduceWindowToSingleDistribution =
          new Func1<Observable<Histogram>, Observable<Histogram>>() {
            @Override
            public Observable<Histogram> call(Observable<Histogram> window) {
              return window.reduce(distributionAggregator);
            }
          };

  private static final Func1<Histogram, CachedValuesHistogram> cacheHistogramValues =
      new Func1<Histogram, CachedValuesHistogram>() {
        @Override
        public CachedValuesHistogram call(Histogram histogram) {
          return CachedValuesHistogram.backedBy(histogram);
        }
      };

  private static final Func1<
          Observable<CachedValuesHistogram>, Observable<List<CachedValuesHistogram>>>
      convertToList =
          new Func1<Observable<CachedValuesHistogram>, Observable<List<CachedValuesHistogram>>>() {
            @Override
            public Observable<List<CachedValuesHistogram>> call(
                Observable<CachedValuesHistogram> windowOf2) {
              return windowOf2.toList();
            }
          };

  protected RollingDistributionStream(
      final HystrixEventStream<Event> stream,
      final int numBuckets,
      final int bucketSizeInMs,
      final Func2<Histogram, Event, Histogram> addValuesToBucket) {
    final List<Histogram> emptyDistributionsToStart = new ArrayList<Histogram>();
    for (int i = 0; i < numBuckets; i++) {
      emptyDistributionsToStart.add(CachedValuesHistogram.getNewHistogram());
    }

    final Func1<Observable<Event>, Observable<Histogram>> reduceBucketToSingleDistribution =
        new Func1<Observable<Event>, Observable<Histogram>>() {
          @Override
          public Observable<Histogram> call(Observable<Event> bucket) {
            return bucket.reduce(CachedValuesHistogram.getNewHistogram(), addValuesToBucket);
          }
        };

    rollingDistributionStream =
        Observable.defer(
                new Func0<Observable<CachedValuesHistogram>>() {
                  @Override
                  public Observable<CachedValuesHistogram> call() {
                    return 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(); // multicast
  }

  public Observable<CachedValuesHistogram> 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);
    }
  }
}