我使用INCR和EXPIRE来实现速率限制,例如每分钟5个请求:
if EXISTS counter
count = INCR counter
else
EXPIRE counter 60
count = INCR counter
if count > 5
print "Exceeded the limit" 但是,可以在最后一秒发送5个请求1分钟,并在2分钟的第一秒发送5个请求,即在2秒内发送10个请求。
如何避免这个问题?
更新:我想出了这个列表实现。这是一个好方法吗?
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发布于 2012-11-01 19:35:52
你可以从“最后一分钟内5个请求”切换到“x分钟内5个请求”。通过这种方式,可以做到:
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"如果你想继续使用“最后一分钟5个请求”,那么你可以这样做
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"如果您有生产约束(尤其是性能约束),那么使用KEYS关键字就是not advised。我们可以使用集合来代替:
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"这些都是伪Ruby代码,但应该会给您一些启发。
发布于 2016-09-11 01:28:59
进行速率限制的标准方法是通过Leaky bucket algorithm。使用计数器的缺点是,用户可以在计数器重置后立即执行一堆请求,即在下一分钟的第一秒内执行5个操作。Leaky bucket算法解决了这个问题。简而言之,您可以使用有序集来存储您的“泄漏存储桶”,并使用操作时间戳作为键来填充它。
查看这篇文章以获得确切的实现:Better Rate Limiting With Redis Sorted Sets
更新:
还有另一种算法,与漏桶相比有一些优势。它被称为Generic Cell Rate Algorithm。下面是它在更高级别上的工作方式,如Rate Limiting, Cells, and GCRA中所述
GCRA的工作原理是通过一个称为“理论到达时间”(TAT)的时间来跟踪剩余限制,该时间是在第一个请求时通过将代表其成本的持续时间添加到当前时间来播种的。成本被计算为我们的“发射间隔”(T)的乘数,该间隔是从我们希望桶重新填充的速率导出的。当任何后续请求传入时,我们获取现有的TAT,从中减去一个表示限制的总突发容量的固定缓冲区(τ+ T),并将结果与当前时间进行比较。此结果表示下一次允许请求的时间。如果是过去的请求,我们允许传入的请求,如果是将来的请求,则不允许。成功请求后,通过添加T来计算新的TAT。
在GitHub上有一个实现此算法的redis模块:https://github.com/brandur/redis-cell
发布于 2016-02-11 16:22:39
这是一个已经回答过的老问题,但这里有一个我从这里得到一些灵感的实现。我正在为Node.js使用ioredis
以下是滚动窗口时间限制器的所有异步但无竞争条件(我希望)的荣誉:
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。如果您看到任何问题,请告诉我。
https://stackoverflow.com/questions/13175050
复制相似问题