关于后端:Redis-实现限流

13次阅读

共计 1797 个字符,预计需要花费 5 分钟才能阅读完成。

常见的限流算法有:计数器,漏桶、令牌桶。

计数器(工夫窗口)

原理:记录每个申请,判断在设定的限流工夫窗口内申请数是否大于限度数。 限流要留神防止边界问题,滑动工夫窗口的办法能很好解决这个问题。

利用 Redis 有序汇合实现,并用管道减速

思路:假如 $period 秒内,一个用户只能拜访 $maxCount 次。用户 ID 作为 key,毫秒工夫戳作为 score 和 value。
一个申请进入,

  1. 退出有序汇合——zadd
  2. 移除工夫窗口之前的行为记录,剩下的都是工夫窗口内的——zremrangebyscore
  3. 更新过期工夫——expire
  4. 获取窗口内的元素数量——zcard
  5. 判断窗口内元素数量是否大于最大申请限度数(maxCount),若大于等于则拒绝请求,若小于则承受申请。
function isActionAllowed($userId, $action, $period, $maxCount) 
{$redis = new Redis();
    $redis->connect('127.0.0.1', 6379);
    $key = sprintf('hist:%s:%s', $userId, $action);
    $now = msectime();   # 毫秒工夫戳

    $pipe=$redis->multi(Redis::PIPELINE); // 应用管道晋升性能
    $pipe->zadd($key, $now, $now); //value 和 score 都应用毫秒工夫戳
    $pipe->zremrangebyscore($key, 0, $now - $period * 1000); // 移除工夫窗口之前的行为记录,剩下的都是工夫窗口内的
    $pipe->zcard($key);  // 获取窗口内的行为数量
    $pipe->expire($key, $period  + 1);  // 多加一秒过期工夫
    $replies = $pipe->exec();
    return $replies[2] <= $maxCount;
}

// 执行
for ($i=0; $i<20; $i++){var_dump(isActionAllowed("110", "reply", 60, 5)); // 执行能够发现只有前 5 次是通过的
}

// 返回以后的毫秒工夫戳
function msectime() {list($msec, $sec) = explode(' ', microtime());
    $msectime = (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
    return $msectime;
 }

漏斗算法

原理:漏桶 (Leaky Bucket) 算法思路很简略,水 (申请) 先进入到漏桶里,漏桶以肯定的速度出水 (接口有响应速率), 当水流入速度过大会间接溢出 (拜访频率超过接口响应速率), 而后就拒绝请求,能够看出漏桶算法能强行限度数据的传输速率。

利用 Redis-Cell 模块实现

Redis 4.0 提供了一个限流 Redis 模块,名称为 redis-cell,该模块提供漏斗算法,并提供原子的限流指令。

该模块只有一条指令 cl.throttle,上面看一下其参数和返回值

> cl.throttle tom:reply 14 30 60 1
1) (integer) 0    # 0 示意容许,1 示意回绝
2) (integer) 15    # 漏斗容量 capacity
3) (integer) 14    # 漏斗残余空间 left_quota
4) (integer) -1    # 如果回绝了,须要多长时间后再重试,单位秒
5) (integer) 2    # 多长时间后,漏斗齐全空进去,单位秒 

令牌桶算法

原理:令牌桶算法 (Token Bucket) 和 Leaky Bucket 成果一样但方向相同的算法,更加容易了解。随着工夫流逝,零碎会按恒定 1/QPS 工夫距离 (如果 QPS=100, 则距离是 10ms) 往桶里退出 Token (设想和破绽漏水相同,有个水龙头在一直的加水), 如果桶曾经满了就不再加了。新申请来长期,会各自拿走一个 Token, 如果没有 Token 可拿了就阻塞或者拒绝服务。

利用 Redis String + 定时工作实现

利用定时工作一直减少令牌数,Redis->incr(),自增一,令牌桶满则不再减少;
来一个申请耗费一个令牌,Redis->decr(),自减一,当没有令牌时则拒绝请求。
一旦须要进步速率,则按需进步放入桶中的令牌的速率即可。

参考 https://learnku.com/articles/…

正文完
 0