写点什么

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

作者:Owen Zhang
  • 2022 年 5 月 27 日
  • 本文字数:2372 字

    阅读完需:约 8 分钟

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

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


不懂的可以评论或联系我邮箱:owen@owenzhang.com\


著作权归 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 亿个元素)。




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 存在但不是列表类型时,返回一个错误。



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

rpop

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



redis 127.0.0.1:6379> rpop keyname
复制代码



令牌桶父类

步骤如下:


  1. 加入令牌


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



  1. 获取令牌




  1. 重设令牌桶,填满令牌




<?php

namespace common\components;

use Yii;
use yii\redis\Connection;

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


添加令牌


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

请求过快


$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



发布于: 刚刚阅读数: 4
用户头像

Owen Zhang

关注

还未添加个人签名 2020.05.10 加入

I actually hate programming, but I love solving problems. Phper & Gopher. https://gitee.com/owenzhang24 Email: owen@owenzhang.com

评论

发布
暂无评论
PHP实现令牌桶限流Redis list列表 Lpush rpop 实现令牌桶 - 限流 PHP实例_php_Owen Zhang_InfoQ写作社区