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

PHP开发实例 2022年8月14日 113

令牌桶限流介绍

令牌桶算法 (Token Bucket) 和 Leaky Bucket 效果一样但方向相反的算法,更加容易理解。
随着时间流逝,系统会按恒定 1/QPS 时间间隔 (如果 QPS=100, 则间隔是 10ms) 往桶里加入 Token (想象和漏洞漏水相反,有个水龙头在不断的加水), 如果桶已经满了就不再加了。
新请求来临时,会各自拿走一个 Token, 如果没有 Token 可拿了就阻塞或者拒绝服务.

  • 令牌桶的另外一个好处是可以方便的改变速度。
  • 一旦需要提高速率,则按需提高放入桶中的令牌的速率。
  • 一般会定时 (比如 1000 毫秒) 往桶中增加一定数量的令牌,有些变种算法则实时的计算应该增加的令牌的数量.

父类

<?php

namespace common\components;

use Yii;
use yii\redis\Connection;

/**
 * 令牌桶 - 限流
 *
 * 令牌桶算法 (Token Bucket) 和 Leaky Bucket 效果一样但方向相反的算法,更加容易理解。
 * 随着时间流逝,系统会按恒定 1/QPS 时间间隔 (如果 QPS=100, 则间隔是 10ms) 往桶里加入 Token (想象和漏洞漏水相反,有个水龙头在不断的加水), 如果桶已经满了就不再加了。
 * 新请求来临时,会各自拿走一个 Token, 如果没有 Token 可拿了就阻塞或者拒绝服务.
 *
 * 令牌桶的另外一个好处是可以方便的改变速度。
 * 一旦需要提高速率,则按需提高放入桶中的令牌的速率。
 * 一般会定时 (比如 1000 毫秒) 往桶中增加一定数量的令牌,有些变种算法则实时的计算应该增加的令牌的数量.
 *
 * Class TrafficShaper
 * @package common\components
 */
class TrafficShaper
{
    /**
     * 令牌桶
     *
     * @var string
     */
    public $container;

    /**
     * 最大令牌数
     *
     * @var int
     */
    public $max;

    /**
     * @var Connection
     */
    protected $redis;

    /**
     * TrafficShaper constructor.
     * @param int $max
     * @param string $container
     */
    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);
    }
}

调用实例

添加令牌

   /**
     * 添加令牌数量
     */
    public function add()
    {
        // 默认最大添加数量为300
        $trafficShaper = new TrafficShaper(300);
        $trafficShaper->add(50);
    }

请求过快

$trafficShaper = new TrafficShaper();
if (!$trafficShaper->get()) {
    throw new TooManyRequestsHttpException('请求过快');
}

转载地址:https://my.oschina.net/owenzhang24/blog/5495382


关注微信公众号『PHP学习网

第一时间了解最新网络动态
关注博主不迷路~

PHP学习网:站内收集的部分资源来源于网络,若侵犯了您的合法权益,请联系我们删除!
分享到:
赞(3)

文章评论

您需要之后才可以评论
0点赞 0评论 收藏 QQ分享 微博分享

PHP学习网

PHP学习网