Blogs

Rate limiting with Redis

The rate limiter pattern is a special counter that is used to limit the rate at which an operation can be performed.

There are a few classes of rate limiting or velocity checking you can do:

  1.  Per user or API key: ensure that any given user account or API account key holder can only perform (n) actions per minute.
  2.  Per IP address: ensure that any given IP address can only perform (n) actions per minute.
  3.  Per global action: ensure that a particular action can only happen (n) times per minute.

A common use case is to limit the amount of requests an IP can make over a certain time frame. For example, you might want to restrict users from using an expensive search utility on your site. If the user attempts to search more than 5 times a minute, you can redirect them to another page informing them that they need to wait.
Google and Yahoo both employ the IP based rate limiting technique to prevent (or at least complicate) automated requests to their services.

GoogleRateLimit

We can use Redis to help us in implementing an rate limiter.

Redis is an open-source, networked, in-memory, key-value data store with optional durability structures and provides some useful features:

  •  A key is automatically created the first time it’s used.
  •  We can create very space-efficient key/value pairs using the built-in hash type.
  •  Redis has a method that atomically increments a hash field.
  •  We can chain commands together using transactions. Redis guarantees that the commands will be run sequentially.

Example
Assume that the problem to solve is limiting the number of API calls to a maximum of ten requests per second per IP address.

The more simple and direct implementation of rate limiter with Redis is the following:


FUNCTION LIMIT_API_CALL(ip)
ts = CURRENT_UNIX_TIME()
keyname = ip+":"+ts
current = GET(keyname)
IF current != NULL AND current > 10 THEN
  ERROR "too many requests per second"
ELSE
  MULTI
   INCR(keyname,1)
   EXPIRE(keyname,10)
  EXEC
  PERFORM_API_CALL()
END

Basically we have a counter for every IP, for every different second. But this counters are always incremented setting an expire of 10 seconds so that they’ll be removed by Redis automatically when the current second is a different one.

The same method in C# becomes:


private void LimitApiCall(string ip)
{
  var ts = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
  var keyname = ip + ":" + ts;
  int current = 0;
  using (var redis = new RedisClient())
  {
   var value = redis.GetValue(keyname);
   int.TryParse(value, out current);
  }
  if(current > 10)
   throw  new Exception("too many requests per second");

  using (var trans = new RedisClient().CreateTransaction())
  {
   trans.QueueCommand(r => r.Increment(keyname, 1));
   trans.QueueCommand(r => r.ExpireEntryIn(keyname, TimeSpan.FromSeconds(subjectExpiry)));

   trans.Commit();
  }
}

 

0


Add a Comment