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
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.
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.
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).
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.
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).
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With