Simple Rate Limiter with Redis

Simple Rate Limiter with Redis

We will create a simple rate limiter with Redis. But first, what is a rate limiter, and why do we use it?

What is a rate limiter?

The rate limiter makes sure the number of processes within a time interval does not exceed a specified threshold. For example, we have a rate limiter that limits the number of requests to 3 requests per second. If there are more than 3 requests in one second, we will deny the request. HTTP code 429 is commonly used to respond to the client if the request got rate-limited.

A rate limiter is needed to protect our infrastructure from overload because of the high number of requests, whether the requests are from genuine clients or bots. It can also be useful for a certain setting, such as a user can only do transaction max 2 times per hour.

There are many algorithms for rate limiter implementation, such as leaky bucket, token bucket, and fixed window. In this article, we will create a rate limiter with fixed window algorithm. We will use Redis to store the counter.

Rate limiter using Redis

To validate the rate limit, we increment a counter every time a request is received. Then we compare the total number of requests in a specified interval with a specified threshold. If the number is bigger than the threshold, we reject the request. To count the request we use the INCR command of the Redis. We set the expiry of the counter to the specified time interval. Below is an example of the rate limiter in go.

import (
    "github.com/gomodule/redigo/redis"
)

var (
    pool *redis.Pool

    intervalSecond    = 60
    requestCountLimit = int64(3)
    redisKey          = "api_request_limit"
)

func handleRequest() error {
    conn := pool.Get()
    defer conn.Close()

    count, err := redis.Int64(conn.Do("INCR", redisKey))
    if err != nil {
        // ...
    }

    if count == 1 {
        _, err := redis.Int(conn.Do("EXPIRE", redisKey, intervalSecond))
        if err != nil {
            // ...
        }
    }

    if count > requestCountLimit {
        log.Println("limit exceeded")
        // ...
    }

    // ...
    log.Println("success")
    return nil
}
In the example above, we limit the request to a maximum of 3 requests in 60 seconds. If the counter is just created, we set the expiry to the specified interval. Once the interval time is passed, the counter is expired and reset to 0. We need to make sure that the counter has an expiry. If not, it will never be reset.

Now lets test it. Send multiple requests at the same time and see the result. Only three requests should success, and the rest should be rejected.

Test Result

We can also rate limit operation per user. We just need to include a user identifier in the Redis key. So that each user has their own counter.

Conclusion

A rate limiter can be used to protect our service from overloading by rejecting requests if it exceeds the limit counter. There are many algorithms for rate limiters. One of them is fixed window algorithm. I think a fixed window rate limiter with Redis is the simplest because we only need to increment the counter, set counter expiry, and compare the counter with our limit.


See also

comments powered by Disqus