我正在尝试编写一个算法来防止在8秒内发送超过5条消息,我在python中找到了令牌桶算法的公式,所以我试图用javascript实现它,但我错过了一件事。
last_check
变量对我来说并不清楚。如果我在一分钟内跟踪60秒,问题就是在第59秒后循环回来时,数学被抛弃了。
第一次迭代
如果last_check
= new Date().getSeconds()
让我们说它跟踪的时间是33秒,我们现在比较的是33秒。
当rateLimit()
函数执行时,它获得调用时的current
时间,例如消息,current
new Date().getSeconds()
为40秒.... last_check
之后7秒
所以time_passed
现在是7秒,last_checked
变成了40。
津贴结果
allowance
为7 * (5.0/8.0) = 4.375
。
问题在哪里
如果last_checked
保持在40,并且在新消息之前经过22秒,那么下面第二次迭代的公式就会变为。
第二次迭代
current
变为2(40 + 22)new Date().getSeconds()
循环回2 ... time_passed
现在(2-40)= -38秒,last_checked
现在是2。
津贴结果
allowance
为-38 * (5.0/8.0) = -23.75
。
var rate = 5.0; // unit: messages
var per = 8.0; // unit: seconds
var allowance = rate; // unit: messages
var last_check = new Date().getSeconds();
function rateLimit() {
current = new Date().getSeconds(); // floating-point, e.g. usec accuracy. Unit: seconds
time_passed = current - last_check;
last_check = current;
allowance += time_passed * (rate / per);
console.log('Current time: ' + current);
console.log('Time passed: ' + time_passed);
console.log('Last check: ' + last_check);
console.log('Allowance: ' + allowance);
if (allowance > rate) {
allowance = rate; // throttle
console.log('throttle');
}
if (allowance < 1.0) {
console.log('discard message');
return false;
} else {
allowance -= 1.0;
console.log('forward message');
return true;
}
}
答案 0 :(得分:2)
我同意@ Evilzebra的评论,即解决方案可能有点复杂。根据您所需的行为,我以这种方式实现:
var ii = 0;
var perMils = per * 1000;
function rateLimit() {
if (ii <= rate) {
++ii;
setTimeout(function() {--ii;}, perMils);
return true;
}
return false;
}
答案 1 :(得分:1)
在current - last_check
中,您需要两个日期之间的差异,而不是秒针的位置之间的差异。
将new Date().getSeconds()
替换为new Date().getTime()/1000
(或new Date().getTime()
,但您必须将var per = 8.0
替换为var per = 8000.0
。
这将导致current - last_check
始终为正秒数(或毫秒),而不是在秒数转为分钟时为负数。
答案 2 :(得分:0)
您也可以使用我写过的flood-protection库。它使用Token Bucket Algorithm
用法:
import FloodProtection from 'flood-protection';
const floodProtection = new FloodProtection({
rate: 5,
// default: 5, unit: messages
// IMPORTANT: rate must be >= 1 (greater than or equal to 1)
per: 8,
// default: 8, unit: seconds
});
希望这有帮助。