Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement rate limiting using Redis

Tags:

I use INCR and EXPIRE to implement rate limiting, e.g., 5 requests per minute:

if EXISTS counter     count = INCR counter else     EXPIRE counter 60     count = INCR counter  if count > 5     print "Exceeded the limit"     

However, 5 requests can be sent at the last second minute one and 5 more requests at the first second of minute two, i.e., 10 requests in two seconds.

How can this problem be avoided?


Update: I came up with this list implementation. Is this a good way to do it?

times = LLEN counter if times < 5     LPUSH counter now() else     time = LINDEX counter -1     if now() - time < 60         print "Exceeded the limit"     else         LPUSH counter now() LTRIM counter 5 
like image 530
luin Avatar asked Nov 01 '12 10:11

luin


People also ask

How is Redis used for rate limiting?

Building a rate limiter with Redis is easy because of two commands INCR and EXPIRE. The basic concept is that you want to limit requests to a particular service in a given time period. Let's say we have a service that has users identified by an API key.

How do you implement the rate limit?

Often rate-limiting is applied at a reverse proxy, API gateway, or load balancer before the request reaches the API, so that it can be applied to all requests arriving at a cluster of servers. By handling this at a proxy server, you also avoid excess load being generated on your application servers.

Can load balancer Do rate limiting?

When rate limiting is performed by the reverse proxy, and when there is a load balancer or other network device terminating traffic and then forwarding onto the reverse proxy, the IP address flag ip: true is not useful. The IP address is effectively static (as it is the address of the network terminating device).

How to limit the number of requests per hour in Redis?

In this tutorial we will build a basic rate limiting feature that allows 10 requests/hour to our service for a logged in user. Redis provides two key commands namely - INCR and EXPIRE that will allow us build this features without much effort.

What is the difference between rate limiter and basic rate limit?

Rate limiter can be used in throttling API requests to a given service. The basic concept is that we want to limit number of requests to a particular service in a given time period. Basic Rate Limiting is an essential feature in any production grade API where we want to limit number of API calls per user per hour (or per minute).

How can I reduce the number of round trips to Redis?

One common method of minimizing the number of round trips to Redis is to use what is called ‘pipelining’. Pipelining in the Redis context will send multiple commands to Redis in a single round trip, which can reduce overall latency.

What are some real world examples of rate limiting?

Few real world examples could be: Allow 20 requests to a service using a given API key (as per plan) Allow user (or IP address) to post max one comment per minute on a blogging site In this tutorial we will build a basic rate limiting feature that allows 10 requests/hour to our service for a logged in user.


1 Answers

You could switch from "5 requests in the last minute" to "5 requests in minute x". By this it would be possible to do:

counter = current_time # for example 15:03 count = INCR counter EXPIRE counter 60 # just to make sure redis doesn't store it forever  if count > 5   print "Exceeded the limit" 

If you want to keep using "5 requests in the last minute", then you could do

counter = Time.now.to_i # this is Ruby and it returns the number of milliseconds since 1/1/1970 key = "counter:" + counter INCR key EXPIRE key 60  number_of_requests = KEYS "counter"*" if number_of_requests > 5   print "Exceeded the limit" 

If you have production constraints (especially performance), it is not advised to use the KEYS keyword. We could use sets instead:

counter = Time.now.to_i # this is Ruby and it returns the number of milliseconds since 1/1/1970 set = "my_set" SADD set counter 1  members = SMEMBERS set  # remove all set members which are older than 1 minute members {|member| SREM member if member[key] < (Time.now.to_i - 60000) }  if (SMEMBERS set).size > 5   print "Exceeded the limit" 

This is all pseudo Ruby code, but should give you the idea.

like image 113
alto Avatar answered Sep 20 '22 13:09

alto