node.js 使用redis做分布式限流
原理分析:
node.js 如果用进程间通信ipc 做分布式限流,部署起来会比较麻烦,要考虑到进程间通信,状态维护等,redis性能足够,做起来相对简单
具体使用方法
redis.setnx 如果当前键存在,则啥也不做就返回0 不存在设置这个键 返回1
redis.getset 设置新值得同时返回旧值
利用redis.getset 来判断当前的请求是否已经被抢占
伪代码如下
let value = await redis.setnx(redisKey,`[timestamps]`);
// 这里第一次请求
if (value) {
// 直接请求成功
return next()
}
// 如果之前已经有请求 拿到历史请求数距
let history = await redis.get(redisKey);
history = JSON.parse(history);
let lastHitTime = history[history.length - 1];
// 如果当前时间大于上次设置时间,
let setTime = new Date().getTime();
if (setTime < lastHitTime) {
return res.status(429).send();
}
history.push(setTime);
if (history.length > params.rateSpeed * 2) {
// 只保留100次请求的信息
history = history.slice(history.length - params.rateSpeed * 2);
}
let oldHistory = await redis.getset(redisKey, JSON.stringify(history));
oldHistory = JSON.parse(oldHistory);
if (oldHistory[oldHistory.length - 1] > setTime) {
// 这里表示有其他进程 设置了时间
// 这个列表维护起来略复杂,直接block 这个请求
// 这里设置的时间不超过20ms 如果出现了抢占直接block好了
// 这里由于block 太多了,导致很多请求被丢弃了。。
return res.status(429).send();
}
let hitHistory = oldHistory.filter(val => parseInt(val / 1000) === currentSecond);
if (hitHistory.length >= params.rateSpeed) {
return res.status(429).send();
}
不过这里的设置会导致极为大量的请求进来,有一部分数据会被额外的丢弃,