首先我们给出限流的定义:

1. 限定某个行为在指定时间内被允许的最大次数

2. 限定某个用户的某个行为在指定时间内被允许的最大次数

其实二者差不多,前面一句是限定所有人,后面的是限定了每个用户

 

注意

1. 这里我们讨论的是简单的限流,即“限定某个行为在指定时间内被允许的最大次数”中的最大次数是比较小

2. 数量级在几次,几十次,或者几百次级别的。至于上万级别,甚至百万级别的,暂不讨论,因为如果每个玩家都保存这么大的量,内存膨胀得太严重,那种数量级需要更复杂的漏斗限流法

3. 漏斗限流法的介绍请参见这篇博客,等待中

 

简单限流的实现原理:(以每个用户60秒内最多允许10次点赞为例)

1. 利用redis的zset数据结构,保存每次行为及对应的时间戳

    key:点赞标示+用户ID, score:当前毫秒时间戳, value:当前毫秒时间戳,我们主要使用的是score,所以value无所谓的

2. 检测时先移除超过时间限定的旧记录,也就是使用函数  ZREMRANGEBYSCORE() 移除60秒之前的记录

3. 再使用函数 zcard() 获得该key的元素个数,若少于限定次数即,少于10次,则可以操作;否则不允许操作

注意:由于是多条

 

代码如下(这里仅给出伪代码)

// 是否允许某个行为
// userid : 玩家id
// actionkey : 行为
// limittime : 限定的秒数
// maxcount : 允许的最大次数
bool isActionAllowed(string userid, string actionkey, int limittime, int maxcount)
{
// zset的key
string key = actionkey + userid;

// 当前毫秒时间戳
uint64_t nowTime = get_current_time();

// 管道
Pipeline pipe = pipelined();
pipe.multi();
// 核心:移除(0 - limittime秒之前)的记录
pipe.zremrangeByScore(key, 0, nowTime - limittime*1000);
//超时时间(加1秒防时间差)
pipe.expire(key, limittime+1);
// 记录的行为个数
int count = pipe.zcard(key);
return count <= maxcount;
}