com.netflix.eventbus.impl.AbstractEventBusStats Maven / Gradle / Ivy
package com.netflix.eventbus.impl;
import com.netflix.servo.monitor.Monitors;
import org.apache.commons.math.stat.StatUtils;
import org.apache.commons.math.stat.descriptive.moment.Mean;
import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
import org.apache.commons.math.stat.descriptive.rank.Percentile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author Nitesh Kant ([email protected])
*/
public abstract class AbstractEventBusStats {
protected static final Logger LOGGER = LoggerFactory.getLogger(EventBusStats.class);
protected long collectionDurationInMillis;
// Static as the compute is CPU only task, so we can share these timers across consumers.
protected static Timer statsSweeper = new Timer(true);
public AbstractEventBusStats(long collectionDurationInMillis) {
this.collectionDurationInMillis = collectionDurationInMillis;
statsSweeper.schedule(new TimerTask() {
@Override
public void run() {
computeTimeIntervalStats();
}
}, collectionDurationInMillis, collectionDurationInMillis);
}
protected void registerMonitors() {
try {
Monitors.registerObject(this);
} catch (Throwable th) {
LOGGER.error("Unable to register to event bus stats to servo.", th);
}
}
protected abstract void computeTimeIntervalStats();
protected class LatencyStats {
// No reads happen on the below fields, it always happens via the computedData ref which is never written.
private int sampleSize;
private double mean;
private double median;
private double percentile_99_5;
private double percentile_99;
private double percentile_90;
private double stddev;
private double max;
private ConcurrentLinkedQueue rawData;
private AtomicReference computedData; // This is to avoid all members to be AtomicDoubles.
protected LatencyStats() {
rawData = new ConcurrentLinkedQueue();
computedData = new AtomicReference(new LatencyStats(this));
}
private LatencyStats(LatencyStats copyFrom) {
sampleSize = copyFrom.sampleSize;
mean = copyFrom.mean;
median = copyFrom.median;
percentile_90 = copyFrom.percentile_90;
percentile_99 = copyFrom.percentile_99;
percentile_99_5 = copyFrom.percentile_99_5;
stddev = copyFrom.stddev;
max = copyFrom.max;
}
protected void addLatency(double latency) {
rawData.add(latency);
}
protected void compute() {
Percentile percentile = new Percentile();
double[] rawDataAsArray = clearRawDataAndGetAsArray();
if (null != rawDataAsArray && rawDataAsArray.length != 0) {
sampleSize = rawDataAsArray.length;
percentile.setData(rawDataAsArray);
percentile_99_5 = percentile.evaluate(99.5);
percentile_99 = percentile.evaluate(99);
percentile_90 = percentile.evaluate(90);
median = Math.max(1d, percentile.evaluate(50));
max = StatUtils.max(rawDataAsArray);
mean = new Mean().evaluate(rawDataAsArray);
stddev = new StandardDeviation().evaluate(rawDataAsArray);
}
computedData.set(getCopyOfComputedData());
}
protected LatencyStats getComputedStats() {
return computedData.get();
}
public int getSampleSize() {
return getComputedStats().sampleSize;
}
public double getMean() {
return getComputedStats().mean;
}
public double getMedian() {
return getComputedStats().median;
}
public double getPercentile_99_5() {
return getComputedStats().percentile_99_5;
}
public double getPercentile_99() {
return getComputedStats().percentile_99;
}
public double getPercentile_90() {
return getComputedStats().percentile_90;
}
public double getStddev() {
return getComputedStats().stddev;
}
public double getMax() {
return getComputedStats().max;
}
private LatencyStats getCopyOfComputedData() {
return new LatencyStats(this);
}
private double[] clearRawDataAndGetAsArray() {
double[] toReturn = new double[rawData.size()];
int index = 0;
for (Iterator iterator = rawData.iterator(); iterator.hasNext(); ) {
Double aDataPoint = iterator.next();
iterator.remove(); // Do not double count in the next cycle.
toReturn[index++] = aDataPoint;
}
return toReturn;
}
}
}