设想用户要求请求一个token(这里的token采用jwt的开发标准生成,详情可以去看看这篇文章http://dwz.cn/6xFGvb,这里不做过多的描述),下面我们来说说怎么解决这个问题:
首先每个人请求一个token都给用户一条公钥和秘钥。下面称之为appid和appsecret。我一开始的设想是直接从数据库读写用户的次数,但这样子的体验在多用户并发的情况下给服务器带来的压力和用户体验并不好,故弃之;其次我想到的是使用一个定时器(node-schedule),设想是用户每次请求api都在每个用户的总可用次数里面-1,然后定时器在每到设定的时间在进行重置,但最终也是放弃了这种方案,原因有是每个用户都要设置一个定时器,对服务器的开销过大;最终,结合第二个方案,我进行了改进,决定使用时间戳加定时器的方式进行限制,每次用户请求时的时间戳与数组里面存储的用户的上一次时间戳进行对比,如果是超过限定时就直接重置,加入没超过就-1次数,直至为小于等于0时直接拒绝请求。实现伪代码如下:
var timestamp = Date.now(); //获取当前时间戳
//数量和时间戳数组
var static_TokenInfo_Array = {
"num" : 10, //请求次数
"timestamp" : timestamp //时间戳
};
//在timestamp存在的情况下的正确处理
try{
var old_timestamp = (Dic.get(appid))["timestamp"];
//判断时间戳是否为同一天
if(new Date(timestamp).toDateString()===new Date(old_timestamp).toDateString()){
var num = Dic.get(appid)["num"];
if(num!=0){
//在是
static_TokenInfo_Array = {
"num" : num-1,
"timestamp" : timestamp
};
//伪代码,写入数组appid是Key,static_TokenInfo_Array是value
Dic.put(appid, static_TokenInfo_Array);
Token_Set();
}
else{
//小于0次直接范围错误400并返回错误处理的Json
return res.status(400).json({error:"Token_Post It can be used 10 times a day"});
}
}
else{
//假设不在同一天直接更新数组
Dic.put(appid, static_TokenInfo_Array);
}
}
//首次写入"timestamp"时间戳不存在的情况下进行的异常处理
catch(error){
var error_str = " 'timestamp' of undefined";
//将错误转换成string并进行排查是否"timestamp"不存在
if((error.toString()).indexOf(error_str)){
//不存在,直接写入
Dic.put(appid, static_TokenInfo_Array);
Token_Set();
}
}
//返回生产token的伪代码
function Token_Set(){
var authToken = jwt.sign({appid: appid}, "secret",{
expiresIn: 60 * 60 * 24 // 过期时间 表示24小时过期
});
res.status(200).json({token: authToken});
}
此种方案既避免和大量读写数据库和大量定时器对服务器造成的巨大开销,假设储存一个用户的请求次数数据为30字节,即使100万个存储用户量也不过30M的开销,开销成本降低了很多。
定时器(node-schedule)功能就不继续写,有需要者可以继续完善。
最后,有兴趣的童鞋可以去看下这两个github,我的灵感来源于这里http://dwz.cn/6xG3Hj 和 http://dwz.cn/6xG4lC
Good Night ——Turkey