Skip to content

Instantly share code, notes, and snippets.

@thomaspoignant
Created April 27, 2020 13:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thomaspoignant/9a44afb8b7befa062a6cd6300a549c68 to your computer and use it in GitHub Desktop.
Save thomaspoignant/9a44afb8b7befa062a6cd6300a549c68 to your computer and use it in GitHub Desktop.
package com.tpoi.monitoring.service;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* HitCounter count the number of request in a specific range of time.
*/
public class HitCounter {
// hits, array where each row is the number of hits for this second,
// we use AtomicInteger because it is thread safe.
private final AtomicInteger[] hits;
// times, array where each row is a timestamp, it is here to keep track
// of which timestamp has already been in the counter in the last X seconds.
private final long[] times;
// rangeInSecond, the number of seconds we need to keep.
private final int rangeInSecond;
/**
* Constructor for a HitCounter
*
* @param rangeInSecond, number of seconds to keep
*/
public HitCounter(final int rangeInSecond) {
this.rangeInSecond = rangeInSecond;
this.times = new long[rangeInSecond];
// Init the array with an AtomicInteger of 0.
this.hits = Stream.generate(() -> new AtomicInteger(0))
.limit(rangeInSecond)
.toArray(AtomicInteger[]::new);
}
/**
* hit, is called for each new request and increment the counter
* for the second of the request.
*
* @param timestamp, the timestamp of the request
*/
public void hit(final long timestamp) {
final int idx = Math.toIntExact(timestamp % rangeInSecond);
if (times[idx] != timestamp) {
// we should re-initialize the number of request
hits[idx].set(1);
times[idx] = timestamp;
return;
}
hits[idx].addAndGet(1);
}
/**
* getHits, compute the number of request in the last X seconds.
*
* @return number of request in the last X seconds.
*/
public long getHits() {
final var timestamp = Instant.now().getEpochSecond();
// We filter to get only the hits from the last X seconds.
return IntStream.range(0, times.length)
.filter(i -> timestamp - times[i] < this.rangeInSecond)
.map(i -> hits[i].get())
.reduce(0, Integer::sum);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment