当我们需要限制单个接口在一定时间内的请求次数,可以通过 Redis 的 zset 类型来实现一个简单的限流。
整体思路是:每一个行为到来时,都维护一次时间窗口,将窗口外的记录全部都清理掉,只保留窗口内的记录。zset 集合中的 score 值非常重要,Value 值没有特别的意义,只要保证唯一就可以了。
这种方式只适合简单的限流,如果限流的量很大,比如:60 秒内操作不能超过 100 万次,就不适合使用窗口限流,因为需要记录窗口内的所有行为,会消耗大量的存储空间。
<?php/*** 滑动窗口限流** @param [type] $user_id 用户id* @param [type] $action 行为* @param [type] $period 滑动窗口宽度(秒)* @param [type] $max_count 限制次数* @return 返回布尔值,true 表示未限制 false 表示限制*/function sliding_window($user_id,$action,$period,$max_count){$key = "hist:{$user_id}:{$action}";$time = time();$redis = new Redis();$redis->connect('127.0.0.1',6379);// 记录行为$redis->zadd($key,$time,md5(microtime()));// 移除掉窗口外的$redis->zremrangebyscore($key,0,$time - $period);// 设置过期时间,防止冷用户持续占用内存// 过期时间应该是时间窗口长度+1s$redis->expire($key,$period + 1);// 取窗口行为数return $redis->zcard($key) <= $max_count;}sliding_window(1,'add',60,5);
