Created
February 11, 2016 08:05
-
-
Save ChisholmKyle/b44fa64d5252eee59041 to your computer and use it in GitHub Desktop.
General purpose rate limiters using Redis and ioredis in node.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Ioredis = require('ioredis'); | |
var redis = new Ioredis(); | |
// Rolling window rate limiter | |
// | |
// key is a unique identifier for the process or function call being limited | |
// exp is the expiry in milliseconds | |
// maxnum is the number of function calls allowed before expiry | |
var redis_limiter_rolling = function(key, maxnum, exp, next) { | |
redis.multi([ | |
['incr', 'limiter:num:' + key], | |
['time'] | |
]).exec(function(err, results) { | |
if (err) { | |
next(err); | |
} else { | |
// unique incremented list number for this key | |
var listnum = results[0][1]; | |
// current time | |
var tcur = (parseInt(results[1][1][0], 10) * 1000) + Math.floor(parseInt(results[1][1][1], 10) / 1000); | |
// absolute time of expiry | |
var texpiry = tcur - exp; | |
// get number of transacation in the last expiry time | |
var listkey = 'limiter:list:' + key; | |
redis.multi([ | |
['zadd', listkey, tcur.toString(), listnum], | |
['zremrangebyscore', listkey, '-inf', texpiry.toString()], | |
['zcard', listkey] | |
]).exec(function(err, results) { | |
if (err) { | |
next(err); | |
} else { | |
// num is the number of calls in the last expiry time window | |
var num = parseInt(results[2][1], 10); | |
if (num <= maxnum) { | |
// does not reach limit | |
next(null, false, num, exp); | |
} else { | |
// limit surpassed | |
next(null, true, num, exp); | |
} | |
} | |
}); | |
} | |
}); | |
}; | |
// Lockout window rate limiter | |
// | |
// key is a unique identifier for the process or function call being limited | |
// exp is the expiry in milliseconds | |
// maxnum is the number of function calls allowed within expiry time | |
var util_limiter_lockout = function(key, maxnum, exp, next) { | |
// lockout rate limiter | |
var idkey = 'limiter:lock:' + key; | |
redis.incr(idkey, function(err, result) { | |
if (err) { | |
next(err); | |
} else { | |
if (result <= maxnum) { | |
// still within number of allowable calls | |
// - reset expiry and allow next function call | |
redis.expire(idkey, exp, function(err) { | |
if (err) { | |
next(err); | |
} else { | |
next(null, false, result); | |
} | |
}); | |
} else { | |
// too many calls, user must wait for expiry of idkey | |
next(null, true, result); | |
} | |
} | |
}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment