redis令牌桶限流
1、频率设置
首先得知道限流的上限,以QPS表示,比如10000/QPS,然后如果每10ms注入一次,那么频率就是
100/10ms,也就是每10ms注入100个令牌。所以需要一个进程来定时做这个事情。
2、实现
<?php
class Token{
private $_config; // redis设定
private $_redis; // redis对象
private $_queue; // 令牌桶
private $_max; // 最大令牌数
/**
* 初始化
* @param Array $config redis连接设定
*/
public function __construct($config, $queue, $max)
{
$this->_config = $config;
$this->_queue = $queue;
$this->_max = $max;
$this->_redis = $this->connect();
}
/**
* 加入令牌
* @param Int $num 加入的令牌数量
* @return Int 加入的数量
*/
public function add($num = 0)
{
// 当前剩余令牌数
$curnum = intval($this->_redis->lSize($this->_queue));
// 最大令牌数
$maxnum = intval($this->_max);
// 计算最大可加入的令牌数量,不能超过最大令牌数
$num = $maxnum >= $curnum + $num ? $num : $maxnum - $curnum;
// 加入令牌
if ($num > 0) {
$token = array_fill(0, $num, 1);
$this->_redis->lPush($this->_queue, ...$token);
return $num;
}
return 0;
}
/**
* 获取令牌
* @return Boolean
*/
public function get()
{
return $this->_redis->rPop($this->_queue) ? true : false;
}
/**
* 重设令牌桶,填满令牌
*/
public function reset()
{
$this->_redis->delete($this->_queue);
$this->add($this->_max);
}
private function connect()
{
try {
$redis = new Redis();
$redis->connect($this->_config['host'], $this->_config['port'], $this->_config['timeout'], $this->_config['reserved'], $this->_config['retry_interval']);
if (empty($this->_config['auth'])) {
$redis->auth($this->_config['auth']);
}
$redis->select($this->_config['index']);
} catch (\RedisException $e) {
throw new Exception($e->getMessage());
return false;
}
return $redis;
}
}
3、调用
$config = array(
'host' => 'localhost',
'port' => 6379,
'index' => 0,
'auth' => '',
'timeout' => 1,
'reserved' => NULL,
'retry_interval' => 100,
);
// 令牌桶容器
$queue = 'mycontainer';
// 最大令牌数
$max = 5;
// 创建TrafficShaper对象
$oTrafficShaper = new TrafficShaper($config, $queue, $max);
// 重设令牌桶,填满令牌
$oTrafficShaper->reset();
// 循环获取令牌,令牌桶内只有5个令牌,因此最后3次获取失败
for ($i = 0; $i < 8; $i++) {
var_dump($oTrafficShaper->get());
}
// 加入10个令牌,最大令牌为5,因此只能加入5个
$add_num = $oTrafficShaper->add(10);
var_dump($add_num);
// 循环获取令牌,令牌桶内只有5个令牌,因此最后1次获取失败
for ($i = 0; $i < 6; $i++) {
var_dump($oTrafficShaper->get());
}
输出:
boolean true
boolean true
boolean true
boolean true
boolean true
boolean false
boolean false
boolean false
int 5
boolean true
boolean true
boolean true
boolean true
boolean true
boolean false