当我们需要限制单个接口在一定时间内的请求次数,可以通过 Redis 的 zset 类型来实现一个简单的限流。

    整体思路是:每一个行为到来时,都维护一次时间窗口,将窗口外的记录全部都清理掉,只保留窗口内的记录。zset 集合中的 score 值非常重要,Value 值没有特别的意义,只要保证唯一就可以了。

    这种方式只适合简单的限流,如果限流的量很大,比如:60 秒内操作不能超过 100 万次,就不适合使用窗口限流,因为需要记录窗口内的所有行为,会消耗大量的存储空间。

    1. <?php
    2. /**
    3. * 滑动窗口限流
    4. *
    5. * @param [type] $user_id 用户id
    6. * @param [type] $action 行为
    7. * @param [type] $period 滑动窗口宽度(秒)
    8. * @param [type] $max_count 限制次数
    9. * @return 返回布尔值,true 表示未限制 false 表示限制
    10. */
    11. function sliding_window($user_id,$action,$period,$max_count)
    12. {
    13. $key = "hist:{$user_id}:{$action}";
    14. $time = time();
    15. $redis = new Redis();
    16. $redis->connect('127.0.0.1',6379);
    17. // 记录行为
    18. $redis->zadd($key,$time,md5(microtime()));
    19. // 移除掉窗口外的
    20. $redis->zremrangebyscore($key,0,$time - $period);
    21. // 设置过期时间,防止冷用户持续占用内存
    22. // 过期时间应该是时间窗口长度+1s
    23. $redis->expire($key,$period + 1);
    24. // 取窗口行为数
    25. return $redis->zcard($key) <= $max_count;
    26. }
    27. sliding_window(1,'add',60,5);