Skip to content

Ferdinand Agyei-Yeboah

Google Guava Rate Limiter

May 15, 2023

Background

Rate limiting is when an API restricts the number of invocations over time for a client (could be a user, another application, etc..). Rate limits are instituted for various reasons; preventing DDOS attacks, forcing premium usage of api, keeping server performant, etc,

Whether you are an api client for a free api that limits your number of apis calls per second, or a developing server side application and want to limit user calls per second, the Google Guava rate limiter can help you.

Implementation

To use google guava, you first must include the dependency for your build tool. Maven shown below.

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>

Using RateLimiter

RateLimiter.create()

To create a rate limiter instance you can use RateLimiter.create(TPS). The parameter determines the TPS you want. Below I create a RateLimiter that handles 2 transactions (requests) per seconds.

RateLimiter rateLimiter = RateLimiter.create(2);

Acquire()

To get a “permit” (execution) from the RateLimiter, you simply call rateLimiter.acquire() and pass the number of executions you want. For example if you have a RateLimiter of 2 and you request a permit of 2, the rateLimiter would allow you to continue executing the subsequent code. However, if the subsequent code took less than a second, say 0.3s, and you tried to execute it again immediately, then it would block until the specified number of permits are available again. (In this case it would block for 0.7s). Similarly, if you acquired 4 permits when no permits are available, then it would block for 2 seconds since 4 permits/2 allowed TPS = 2 seconds. You can change the number of permits acquired for an operation to change its effective TPS. For example, you may want a TPS of 5 for the create job function, but a TPS of 100 for the read job function. You could achieve this by creating a 100 TPS rate limit, and requiring 20 permits for the create job function and 1 TPS for the read job function. (Note: you would want to use different rate limiter instances if you wanted those individual TPSes for the two actions meaning total TPS = 100 + 20 = 120 TPS, but would use the same rate limiter instance if you wanted 100 TPS to be the combined TPS across the two operations).

TryAcquire()

TryAcquire is similar to acquire() except that it will not block and wait if you cannot receive the specified permits. Instead, it will return true or false (whether you acquired the permits or not), which will allow you to throw an exception if the permits were not acquired. This is opposed to just having the code (and consequently client) wait longer when the permits are not acquired.

Example Usage

Creating a 5 TPS RateLimiter and using it to rate limit emailing to 2.5 TPS

// Create 1 TPS rate limiter
RateLimiter rateLimiter = RateLimiter.create(5);
for (String email: emails) {
// Acquire 1 permit, meaning this loop will execute at 2.5 TPS. TPS = base TPS rate/number of permits per operation = 5/2 = 2.5 TPS
rateLimiter.acquire(2);
emailClient(email)
}

Software Engineering Tutorials & Best Practices