build.tmp.org.xbib.metrics.common.Meter Maven / Gradle / Ivy
package org.xbib.metrics.common;
import org.xbib.metrics.api.Clock;
import org.xbib.metrics.api.Metered;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
/**
* A meter metric which measures mean throughput and one-, five-, and fifteen-minute
* exponentially-weighted moving average throughputs.
*
* @see ExpWeightedMovingAverage
*/
public class Meter implements Metered {
private static final long TICK_INTERVAL = TimeUnit.SECONDS.toNanos(5);
private final ScheduledExecutorService executorService;
private final ExpWeightedMovingAverage m1Rate;
private final ExpWeightedMovingAverage m5Rate;
private final ExpWeightedMovingAverage m15Rate;
private final LongAdder count;
private final AtomicLong lastTick;
private final Clock clock;
private long startedAt;
private ScheduledFuture> future;
/**
* Creates a new {@link Meter}.
* @param executorService the executor service
*/
public Meter(ScheduledExecutorService executorService) {
this(executorService, UserTimeClock.defaultClock());
}
/**
* Creates a new {@link Meter}.
*
* @param executorService the executor service
* @param clock the clock to use for the meter ticks
*/
public Meter(ScheduledExecutorService executorService, Clock clock) {
this.executorService = executorService;
this.clock = clock;
this.lastTick = new AtomicLong(startedAt);
m1Rate = ExpWeightedMovingAverage.oneMinuteEWMA();
m5Rate = ExpWeightedMovingAverage.fiveMinuteEWMA();
m15Rate = ExpWeightedMovingAverage.fifteenMinuteEWMA();
count = new LongAdder();
}
public void start(long intervalSeconds) {
this.startedAt = this.clock.getTick();
this.future = executorService
.scheduleAtFixedRate(this::tickIfNecessary, intervalSeconds, intervalSeconds, TimeUnit.SECONDS);
}
public void stop() {
future.cancel(false);
}
public void shutdown() {
stop();
executorService.shutdownNow();
}
/**
* Mark the occurrence of an event.
*/
public void mark() {
mark(1);
}
/**
* Mark the occurrence of a given number of events.
*
* @param n the number of events
*/
public void mark(long n) {
tickIfNecessary();
count.add(n);
m1Rate.update(n);
m5Rate.update(n);
m15Rate.update(n);
}
private void tickIfNecessary() {
final long oldTick = lastTick.get();
final long newTick = clock.getTick();
final long age = newTick - oldTick;
if (age > TICK_INTERVAL) {
final long newIntervalStartTick = newTick - age % TICK_INTERVAL;
if (lastTick.compareAndSet(oldTick, newIntervalStartTick)) {
final long requiredTicks = age / TICK_INTERVAL;
for (long i = 0; i < requiredTicks; i++) {
m1Rate.tick();
m5Rate.tick();
m15Rate.tick();
}
}
}
}
@Override
public long getCount() {
return count.sum();
}
@Override
public double getFifteenMinuteRate() {
tickIfNecessary();
return m15Rate.getRate(TimeUnit.SECONDS);
}
@Override
public double getFiveMinuteRate() {
tickIfNecessary();
return m5Rate.getRate(TimeUnit.SECONDS);
}
@Override
public double getMeanRate() {
if (getCount() == 0) {
return 0.0;
} else {
final double elapsed = clock.getTick() - startedAt;
return getCount() / elapsed * TimeUnit.SECONDS.toNanos(1);
}
}
@Override
public double getOneMinuteRate() {
tickIfNecessary();
return m1Rate.getRate(TimeUnit.SECONDS);
}
public long elapsed() {
return clock.getTick() - startedAt;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy