Skip to content

FAQ ​

Is this a distributed rate limiter? ​

No. One limiter is one counter in one JVM, so with three instances "100 per second" means 100 per second per instance. That is the right behaviour for protecting a process's own resources, and it is the reason the library has no Redis client, no network call and no configuration server.

For a cluster-wide limit, combine this limiter with a shared counter: the local limiter absorbs bursts with no latency, and a Redis INCR + PEXPIRE (or a gateway limit) provides the global ceiling.

How is this different from Guava's RateLimiter? ​

java-limiterGuava RateLimiter
DependenciesnoneGuava
Lockinglock-free CASsynchronized on an internal mutex
Permits argumentintint (stored permit), double (permits per second)
Rolling-window limityes (SlidingWindowLimiter)no
Alignment / fixed windowyesno
Warm-upyes (WarmUpTokenBucketLimiter, fixed cold factor 3)yes (SmoothWarmingUp)
Concurrency bulkheadyes (ConcurrencyLimiter)no
IntrospectionavailablePermits()none

Guava's is a smooth rate limiter; here that role belongs to LeakyBucketLimiter, and the token bucket, sliding window, fixed window, warm-up and concurrency variants are additions. If Guava is already on your classpath and you only need smooth pacing, Guava is a reasonable choice. If you want no dependency, no mutex, and window, warm-up or bulkhead behaviour, this library covers it.

Why is there no logging and no metrics integration? ​

Both would need a dependency (slf4j-api, micrometer-core) and a configuration surface. Instead every limiter carries a name, and you wire metrics in one line:

java
metrics.gauge("limiter.available", Tags.of("limiter", limiter.name()), limiter, RateLimiter::availablePermits);

Why no @RateLimit annotation or AOP support? ​

Annotations would require Spring or AspectJ on the classpath, and AOP proxies add indirection to a call that is supposed to cost one atomic operation. An annotation-based facade over a Map<String, RateLimiter> is a dozen lines of your own code if you want it; see docs/en/guide → Per-key limiting.

Do I need to close a limiter? Is anything scheduled? ​

Nothing to close and nothing scheduled. There is no thread, no timer, no buffer and no connection. A limiter holds a handful of longs and disappears with the class loader.

What happens if the system clock changes? ​

Nothing. All implementations read System.nanoTime(), which is monotonic, so an NTP jump or a manual clock change cannot make a limiter grant a burst or stall.

What is the memory footprint? ​

LimiterHeap
TokenBucketLimiter, LeakyBucketLimiterone AtomicLong
FixedWindowLimiterone AtomicLong
SlidingWindowLimiterAtomicLongArray(slotCount), 10 longs by default
WarmUpTokenBucketLimiterone AtomicLong plus the ramp start timestamp
ConcurrencyLimiterone AtomicInteger

The jar is about 18 KB and the whole addressable API is 9 classes.

Does it work on Java 17, 21 and 25? ​

Yes. The artifact is Java 8 bytecode (class file version 52) with Automatic-Module-Name: io.github.onblog.limiter, so it works on the classpath and the module path from Java 8 through the current release, virtual threads included.

Is the limit exact? ​

For TokenBucketLimiter, LeakyBucketLimiter and FixedWindowLimiter the admitted count is exact: the check and the update happen in one compare-and-set. SlidingWindowLimiter fails closed under contention, so it can reject a request during a microsecond of racing but never exceeds the limit.

The rate is as accurate as the operating system's timer. Above a few hundred permits per second that is exact in practice; for very low rates (a few per hour) a permit may arrive one scheduling tick late.

Can I use one limiter for several endpoints? ​

You can, but do not: a shared limiter means traffic to one endpoint consumes the budget of the others, and the effective limit of each endpoint drops as neighbours get busy. One limiter per protected resource (and, with per-key limiting, one map of limiters per protected dimension).

Found a bug or need another algorithm? ​

Open an issue at https://github.com/onblog/java-limiter/issues — Chinese or English, both are fine.