利用Redis实现访问速率限制(利用redis实现限速)

  随着Web2.0的发展,越来越多的应用软件都会以一定的频率调用某个网站的API使用服务,为了避免滥用资源,限制访问量是一项必要的操作。在前面,一种常用的限制方式是纪录每个客户每分钟/每秒/每小时访问的次数,并且满足条件时做限制,或者返回特定的状态码。在应用中,我们可以使用Redis的“滑动窗口”实现实时的控制访问,而不用减少实际的服务性能。

  Redis的滑动窗口采用例如一秒钟/一分钟/一小时等窗口,在窗口中将存储请求连接数量,当窗口满足阈值就会触发预设条件。在需要检查客户访问情况的地方,调用Redis的setnx或者incrby来将原子操作添加到窗口中,然后每次访问都会根据窗口过期时间和阈值限制是否能允许该次访问。

//浏览器中


let limitsRate = document.querySelector(".limits-rate");
let myRateLimiter = new RateLimiter(key, max, duration);
if (myRateLimiter.increment()) {
// 表示可以访问
} else {
limitsRate.innerText = myRateLimiter.getResetTime()
}

const redis = require('redis');
const client = redis.createClient({host:"127.0.0.1"})

class RateLimiter{
constructor(key,max,duration){
this.key = key // {ip}
this.max = max // max request per
this.duration = duration // 时间间隔,如果是将其设置为一分钟,则在一分钟内最多执行max次
this._lock = false
}
lock(){
return new Promise(async (resolve)=>{
if(!this._lock){
this._lock = true
resolve(true);
}
else{
setTimeout(async ()=>{
resolve(awt this.lock())
},1)
}
})
}

async increment(){
try{
// 加锁
awt this.lock();
// 释放锁
this._lock = false
let time = Date.now()
let curErpDay = ~~(time/1000/this.duration)*this.duration
client.exists('rateLimiter:' + this.key,async (err,res)=>{
if(res==1){
client.get('rateLimiter:' + this.key,async (err,res)=>{
let rateLimitValue = JSON.parse(res);
if(rateLimitValue.erpTime == curErpDay){
rateLimitValue.val += 1;
if(rateLimitValue.val
client.set('rateLimiter:' + this.key,JSON.stringify(rateLimitValue));
return 1;
}else{
return 0;
}
}else{
rateLimitValue.erpTime = curErpDay;
rateLimitValue.val = 1;
client.set('rateLimiter:' + this.key,JSON.stringify(rateLimitValue));
return 1;
}
})
}else{
let rateLimitValue = {"erpTime":curErpDay,"val":1};
client.set('rateLimiter:' + this.key,JSON.stringify(rateLimitValue));
return 1;
}
})
}catch(e){
console.log(e);
return 0;
}
}
getResetTime(){
let time = (Date.now()/1000/this.duration + 1)*this.duration - Date.now()/1000
time = time.toFixed(3)
return time
}
}

  以上的操作只是使用Redis的当前时间窗口和原子递增技术来控制访问量,在每一次请求的时候都会检查窗口里的值是否超出条件,避免了记录以往的记录耗费大量的内存空间,以及定时更新的缓慢速度,也可以更加紧急及时调整连接频次,从而节省了大量时间和系统资源,让访问量能够适应动态环境变化。


数据运维技术 » 利用Redis实现访问速率限制(利用redis实现限速)