这是一个已经回答了的老问题,但这里有一个实现,我从这里采取了一些灵感。我使用的是ioredis对Node.js的
这里是它的所有异步滚动窗口时间限制器但竞争条件免费(我希望)荣耀:
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);
}
}
});
};
Here's a gist of the functions。如果您发现任何问题,请告知我。
是的,这是一个有效的和良好的解决方案。甚至比使用集合更好;) – alto
在您的解决方案中,now()在Redis LUA脚本中不受支持吗?所以你想通过now()作为参数,那时不同的机器会有不同的毫秒粒度rit ..?所以现在() - 时间不准确? –
对于第二个例子,我估计大约120秒后'计数器'有意义,特别是如果你有很多'计数器'键。 – keune