Problem
In many applications, access to expensive resources must be limited, at which point the rate limit is essential. Many modern Web applications run on multiple processes and servers, and the state needs to be shared. An ideal solution should be efficient and fast, rather than relying on a single application server that is bound to a particular client (due to load balancing) or holding any State itself.
Solution
A simple and effective way to achieve this is to use Redis, which has many useful data structures and functions, although the rate limit requires only 2 functions: First, increment an integer on a specific key value, and set the expiration time for this key value.
Because Redis has a single event circulatory system (each person can only perform one operation at a time each at once), this is an atomic operation, which means that no matter how many clients interact simultaneously, there is always a definite value for the same key value.
This is usually advantageous in cases where multiple rate limits are imposed on the same resource, as this allows for a small number of breaks and longer term limits. For example, request 3 times per second, no minute request 20 times. Because each limit is relatively independent, this requires separate increments from other restrictions.
Because rate limits are often used in resources that are more important in response time (such as Web applications), it is necessary to shorten the usage time of the rate limit as much as possible. The most basic application of Redis is to issue commands, wait for responses, and then issue another command, and so on. This cost is expensive because it requires multiple trips across the network between the application and the Redis server. Because there is no command in this use case to rely on the execution results of other commands, this makes it possible to use a redis called pipelining technology. This is the client caches all Redis requests, and then sends the write request to Redis,redis to return all the results at once.
Redis does not maintain the restrictions required by the client because Redis deletes the old count based on the expiration time set by the client. This eliminates the need for client-side co-ordination and the possibility of removing competitive conditions.
The Code
Import Redis
import time
def rate_limit_check (R, Key, limits):
period_lengths = [_[0] for _ in sorted (limits. Items ())]
period_limits = [_[1] for _ in sorted (Limits.items ())]
pipe = R.pipeline () to
period_length in Iod_lengths:
current_period = Int (time.time ()/period_length)
Redis_key = ' Rate_limit:{key}:{period_length} : {current_period} '. Format (Key=key, Period_length=period_length, Current_period=current_period)
pipe.incr ( Redis_key). Expire (Redis_key, period_length*3) return to No
(hits > Period_limit for Period_limit, hits in Zip ( Period_limits, Pipe.execute () [:: 2]))
if __name__ = = ' __main__ ':
r = Redis. Redis ()
print Rate_limit_check (R, ' 127.0.0.1 ', {1:3, 60:20})
{1:3, 60:20} means that the hit rate 3 times per second is allowed, and 20 hits are allowed under any limit. The ' 127.0.0.1 ' is used here as a key value, although in real life, it might be an IP address. More advanced use cases will have a full application rate limit, the key value is only the client's IP address, and a specific endpoint limit set for expensive endpoints, which will use the client's IP address and endpoint, such as 127.0.0.1+/login/. These restrictions can be set independently.
Return Rate_limit_check (R, ' 127.0.0.1 ', {1:3, 60:20}) and Rate_limit_check (R, ' 127.0.0.1+/login/', {1:2, 60:5})
This is an example written in Python that can be simply ported to any language, as long as the language contains Redis client libraries.