PHP实现令牌桶限流Redis list列表 Lpush rpop 实现令牌桶 - 限流 PHP实例

2022-05-27 10:24:45 浏览数 (2)

本文环境 Windows10,PHP7.1,Redis6.0,Yii 2.0

著作权归OwenZhang所有。商业转载请联系OwenZhang获得授权,非商业转载请注明出处。

令牌桶限流介绍

令牌桶算法 (Token Bucket) 和 Leaky Bucket 效果一样但方向相反的算法,更加容易理解。

随着时间流逝,系统会按恒定 1/QPS 时间间隔 (如果 QPS=100, 则间隔是 10ms) 往桶里加入 Token (想象和漏洞漏水相反,有个水龙头在不断的加水), 如果桶已经满了就不再加了。

新请求来临时,会各自拿走一个 Token, 如果没有 Token 可拿了就阻塞或者拒绝服务.

* 令牌桶的另外一个好处是可以方便的改变速度。

* 一旦需要提高速率,则按需提高放入桶中的令牌的速率。

* 一般会定时 (比如 1000 毫秒) 往桶中增加一定数量的令牌,有些变种算法则实时的计算应该增加的令牌的数量.

Redis list lpush rpop 介绍

list

Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)

一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

代码语言:txt复制
redis 127.0.0.1:6379> lpush keyname owen

(integer) 1

redis 127.0.0.1:6379> lpush keyname zhang

(integer) 2

redis 127.0.0.1:6379> lpush keyname redis

(integer) 3

redis 127.0.0.1:6379> lrange keyname 0 10



1) "owen"

2) "zhang"

3) "redis"

**LPUSH** 将三个值插入了名为 **runoobkey** 的列表当中

lpush

lpush 将一个或多个值插入到列表头部。 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。 当 key 存在但不是列表类型时,返回一个错误。

代码语言:txt复制
redis 127.0.0.1:6379> lpush keyname value1..

rpop

rpop 移除列表的最后一个元素,返回值为移除的元素。

代码语言:txt复制
redis 127.0.0.1:6379> rpop keyname 

令牌桶父类

步骤如下:

  1. 加入令牌

注意:需要加入定时任务,定时增加令牌数量

  1. 获取令牌
  2. 重设令牌桶,填满令牌
代码语言:php复制
<?php



namespace commoncomponents;



use Yii;

use yiiredisConnection;



/**

 * 令牌桶 - 限流

 *

 * 令牌桶算法 (Token Bucket) 和 Leaky Bucket 效果一样但方向相反的算法,更加容易理解。

 * 随着时间流逝,系统会按恒定 1/QPS 时间间隔 (如果 QPS=100, 则间隔是 10ms) 往桶里加入 Token (想象和漏洞漏水相反,有个水龙头在不断的加水), 如果桶已经满了就不再加了。

 * 新请求来临时,会各自拿走一个 Token, 如果没有 Token 可拿了就阻塞或者拒绝服务.

 *

 * 令牌桶的另外一个好处是可以方便的改变速度。

 * 一旦需要提高速率,则按需提高放入桶中的令牌的速率。

 * 一般会定时 (比如 1000 毫秒) 往桶中增加一定数量的令牌,有些变种算法则实时的计算应该增加的令牌的数量.

 *

 * Class TrafficShape

 * @package commoncomponents

 */

class TrafficShape

{

    /**

     * 令牌桶

     *

     * @var string

     */

    public $container;



    /**

     * 最大令牌数

     *

     * @var int

     */

    public $max;



    /**

     * @var Connection

     */

    protected $redis;



    /**

     * TrafficShaper constructor.

     * @param int $max

     * @param string $containe

     */

    public function __construct($max = 300, $container = 'container')

    {

        $this->redis = Yii::$app->redis;

        $this->max = $max;

        $this->container = $container;

    }



    /**

     * 加入令牌

     *

     * 注意:需要加入定时任务,定时增加令牌数量

     *

     * @param int $num 加入的令牌数量

     * @return int 加入的数量

     */

    public function add($num = 0)

    {

        // 当前剩余令牌数

        $curnum = intval($this->redis->llen($this->container));

        // 最大令牌数

        $maxnum = intval($this->max);

        // 计算最大可加入的令牌数量,不能超过最大令牌数

        $num = $maxnum >= $curnum   $num ? $num : $maxnum - $curnum;

        // 加入令牌

        if ($num > 0) {

            $token = array_fill(0, $num, 1);

            $this->redis->lpush($this->container, ...$token);

            return $num;

        }



        return 0;

    }



    /**

     * 获取令牌

     *

     * @return bool

     */

    public function get()

    {

        return $this->redis->rpop($this->container) ? true : false;

    }



    /**

     * 重设令牌桶,填满令牌

     */

    public function reset()

    {

        $this->redis->lrem($this->container, 0, $this->max);

        $this->add($this->max);

    }

}

调用实例

步骤如下:

  1. 添加令牌数量

注意:需要加入定时任务,定时增加令牌数量

  1. 验证令牌桶请求过快

一般会定时 (比如 1000 毫秒) 往桶中增加一定数量的令牌,有些变种算法则实时的计算应该增加的令牌的数量.

添加令牌

代码语言:php复制
/**

     * 添加令牌数量

     */

    public function add()

    {

        // 默认最大添加数量为300

        $trafficShaper = new TrafficShaper(300);

        $trafficShaper->add(50);

    }

请求过快

代码语言:php复制
$trafficShaper = new TrafficShaper();

        if (!$trafficShaper->get()) {

            throw new TooManyRequestsHttpException('请求过快');

        }

Buy me a cup of coffee :)

Buy me a cup of coffee :)

https://www.owenzhang.com/wechat_reward.png

0 人点赞