@Beta @GwtIncompatible public abstract class RateLimiter extends Object
acquire()
blocks if necessary until a permit is available, and then takes it. Once
acquired, permits need not be released.
RateLimiter
is safe for concurrent use: It will restrict the total rate of calls from
all threads. Note, however, that it does not guarantee fairness.
Rate limiters are often used to restrict the rate at which some physical or logical resource
is accessed. This is in contrast to Semaphore
which restricts the
number of concurrent accesses instead of the rate (note though that concurrency and rate are
closely related, e.g. see Little's
Law).
A RateLimiter
is defined primarily by the rate at which permits are issued. Absent
additional configuration, permits will be distributed at a fixed rate, defined in terms of
permits per second. Permits will be distributed smoothly, with the delay between individual
permits being adjusted to ensure that the configured rate is maintained.
It is possible to configure a RateLimiter
to have a warmup period during which time
the permits issued each second steadily increases until it hits the stable rate.
As an example, imagine that we have a list of tasks to execute, but we don't want to submit more than 2 per second:
final RateLimiter rateLimiter = RateLimiter.create(2.0); // rate is "2 permits per second"
void submitTasks(List<Runnable> tasks, Executor executor) {
for (Runnable task : tasks) {
rateLimiter.acquire(); // may wait
executor.execute(task);
}
}
As another example, imagine that we produce a stream of data, and we want to cap it at 5kb per second. This could be accomplished by requiring a permit per byte, and specifying a rate of 5000 permits per second:
final RateLimiter rateLimiter = RateLimiter.create(5000.0); // rate = 5000 permits per second
void submitPacket(byte[] packet) {
rateLimiter.acquire(packet.length);
networkService.send(packet);
}
It is important to note that the number of permits requested never affects the
throttling of the request itself (an invocation to acquire(1)
and an invocation to acquire(1000)
will result in exactly the same throttling, if any), but it affects the throttling
of the next request. I.e., if an expensive task arrives at an idle RateLimiter, it will be
granted immediately, but it is the next request that will experience extra throttling,
thus paying for the cost of the expensive task.
Modifier and Type | Method and Description |
---|---|
double |
acquire()
Acquires a single permit from this
RateLimiter , blocking until the request can be
granted. |
double |
acquire(int permits)
Acquires the given number of permits from this
RateLimiter , blocking until the request
can be granted. |
static RateLimiter |
create(double permitsPerSecond)
Creates a
RateLimiter with the specified stable throughput, given as "permits per
second" (commonly referred to as QPS, queries per second). |
static RateLimiter |
create(double permitsPerSecond,
long warmupPeriod,
TimeUnit unit)
Creates a
RateLimiter with the specified stable throughput, given as "permits per
second" (commonly referred to as QPS, queries per second), and a warmup period,
during which the RateLimiter smoothly ramps up its rate, until it reaches its maximum
rate at the end of the period (as long as there are enough requests to saturate it). |
double |
getRate()
Returns the stable rate (as
permits per seconds ) with which this RateLimiter is
configured with. |
void |
setRate(double permitsPerSecond)
Updates the stable rate of this
RateLimiter , that is, the permitsPerSecond
argument provided in the factory method that constructed the RateLimiter . |
String |
toString()
Returns a string representation of the object.
|
boolean |
tryAcquire()
Acquires a permit from this
RateLimiter if it can be acquired immediately without
delay. |
boolean |
tryAcquire(int permits)
Acquires permits from this
RateLimiter if it can be acquired immediately without delay. |
boolean |
tryAcquire(int permits,
long timeout,
TimeUnit unit)
Acquires the given number of permits from this
RateLimiter if it can be obtained
without exceeding the specified timeout , or returns false immediately (without
waiting) if the permits would not have been granted before the timeout expired. |
boolean |
tryAcquire(long timeout,
TimeUnit unit)
Acquires a permit from this
RateLimiter if it can be obtained without exceeding the
specified timeout , or returns false immediately (without waiting) if the permit
would not have been granted before the timeout expired. |
public static RateLimiter create(double permitsPerSecond)
RateLimiter
with the specified stable throughput, given as "permits per
second" (commonly referred to as QPS, queries per second).
The returned RateLimiter
ensures that on average no more than permitsPerSecond
are issued during any given second, with sustained requests being smoothly
spread over each second. When the incoming request rate exceeds permitsPerSecond
the
rate limiter will release one permit every (1.0 / permitsPerSecond)
seconds. When the
rate limiter is unused, bursts of up to permitsPerSecond
permits will be allowed, with
subsequent requests being smoothly limited at the stable rate of permitsPerSecond
.
permitsPerSecond
- the rate of the returned RateLimiter
, measured in how many
permits become available per secondIllegalArgumentException
- if permitsPerSecond
is negative or zeropublic static RateLimiter create(double permitsPerSecond, long warmupPeriod, TimeUnit unit)
RateLimiter
with the specified stable throughput, given as "permits per
second" (commonly referred to as QPS, queries per second), and a warmup period,
during which the RateLimiter
smoothly ramps up its rate, until it reaches its maximum
rate at the end of the period (as long as there are enough requests to saturate it). Similarly,
if the RateLimiter
is left unused for a duration of warmupPeriod
, it
will gradually return to its "cold" state, i.e. it will go through the same warming up process
as when it was first created.
The returned RateLimiter
is intended for cases where the resource that actually
fulfills the requests (e.g., a remote server) needs "warmup" time, rather than being
immediately accessed at the stable (maximum) rate.
The returned RateLimiter
starts in a "cold" state (i.e. the warmup period will
follow), and if it is left unused for long enough, it will return to that state.
permitsPerSecond
- the rate of the returned RateLimiter
, measured in how many
permits become available per secondwarmupPeriod
- the duration of the period where the RateLimiter
ramps up its rate,
before reaching its stable (maximum) rateunit
- the time unit of the warmupPeriod argumentIllegalArgumentException
- if permitsPerSecond
is negative or zero or warmupPeriod
is negativepublic final void setRate(double permitsPerSecond)
RateLimiter
, that is, the permitsPerSecond
argument provided in the factory method that constructed the RateLimiter
. Currently
throttled threads will not be awakened as a result of this invocation, thus they do not
observe the new rate; only subsequent requests will.
Note though that, since each request repays (by waiting, if necessary) the cost of the
previous request, this means that the very next request after an invocation to setRate
will not be affected by the new rate; it will pay the cost of the previous request,
which is in terms of the previous rate.
The behavior of the RateLimiter
is not modified in any other way, e.g. if the RateLimiter
was configured with a warmup period of 20 seconds, it still has a warmup period of
20 seconds after this method invocation.
permitsPerSecond
- the new stable rate of this RateLimiter
IllegalArgumentException
- if permitsPerSecond
is negative or zeropublic final double getRate()
permits per seconds
) with which this RateLimiter
is
configured with. The initial value of this is the same as the permitsPerSecond
argument
passed in the factory method that produced this RateLimiter
, and it is only updated
after invocations to setRate(double).@CanIgnoreReturnValue public double acquire()
RateLimiter
, blocking until the request can be
granted. Tells the amount of time slept, if any.
This method is equivalent to acquire(1)
.
void
return type})@CanIgnoreReturnValue public double acquire(int permits)
RateLimiter
, blocking until the request
can be granted. Tells the amount of time slept, if any.permits
- the number of permits to acquireIllegalArgumentException
- if the requested number of permits is negative or zerovoid
return type})public boolean tryAcquire(long timeout, TimeUnit unit)
RateLimiter
if it can be obtained without exceeding the
specified timeout
, or returns false
immediately (without waiting) if the permit
would not have been granted before the timeout expired.
This method is equivalent to tryAcquire(1, timeout, unit)
.
timeout
- the maximum time to wait for the permit. Negative values are treated as zero.unit
- the time unit of the timeout argumenttrue
if the permit was acquired, false
otherwiseIllegalArgumentException
- if the requested number of permits is negative or zeropublic boolean tryAcquire(int permits)
RateLimiter
if it can be acquired immediately without delay.
This method is equivalent to tryAcquire(permits, 0, anyUnit)
.
permits
- the number of permits to acquiretrue
if the permits were acquired, false
otherwiseIllegalArgumentException
- if the requested number of permits is negative or zeropublic boolean tryAcquire()
RateLimiter
if it can be acquired immediately without
delay.
This method is equivalent to tryAcquire(1)
.
true
if the permit was acquired, false
otherwisepublic boolean tryAcquire(int permits, long timeout, TimeUnit unit)
RateLimiter
if it can be obtained
without exceeding the specified timeout
, or returns false
immediately (without
waiting) if the permits would not have been granted before the timeout expired.permits
- the number of permits to acquiretimeout
- the maximum time to wait for the permits. Negative values are treated as zero.unit
- the time unit of the timeout argumenttrue
if the permits were acquired, false
otherwiseIllegalArgumentException
- if the requested number of permits is negative or zeropublic String toString()
java.lang.Object
toString
method returns a string that
"textually represents" this object. The result should
be a concise but informative representation that is easy for a
person to read.
It is recommended that all subclasses override this method.
The toString
method for class Object
returns a string consisting of the name of the class of which the
object is an instance, the at-sign character `@
', and
the unsigned hexadecimal representation of the hash code of the
object. In other words, this method returns a string equal to the
value of:
getClass().getName() + '@' + Integer.toHexString(hashCode())
Copyright © 2010–2020. All rights reserved.