MENU

Redis 时间窗口限流

February 17, 2022 • Read: 1294 • PHP,编码

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

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

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

PHP实现代码:

<?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);

笔记内容来自:《Redis深度历险:核心原理与应用实战》