redis实现限流(list数据结构和令牌桶算法)
1.基于Redis的数据结构list
1.1实现流程
我们可以将请求打造成一个list列表,当每一次请求进来的时候,value用当前时间戳表示。可以清楚判断一段时间的访问次数是否超过限制。
2.代码实现
<?php
/**
* 限流:一分钟一个ip只能访问10次
*
*
*/
$redis=new redis();
$redis->connect('127.0.0.1',6379);
$key=$_SERVER["REMOTE_ADDR"];//获取客户端IP地址
$limit=10;//一分钟访问最大次数
if($redis->lLen($key)<$limit){
//判断这个ip一分钟访问是否超过10次
$redis->lPush($key,time()); //将ip访问的时间节点放在队列中
$redis->expire($key,60); //设置IP的有效期为60s
var_dump($redis->lRange($key,0,-1));//返回列表中全部元素
}else{
$lastTime=$redis->lIndex($key,0);//放回列表中第一个元素,即最后一个进入队列的元素
var_dump(date('Y-m-d H:i:s',time()),date('Y-m-d H:i:s',$lastTime));
if(time()-$lastTime<60){
//当前时间-最后一次访问的时间<限制时间
exit('操作太频繁');
}
}
2.基于Redis的令牌桶算法
2.1实现流程
1.首先设有一个令牌桶,桶内存放令牌,一开始令牌桶内的令牌是满的(桶内令牌的数量可根据服务器情况设定)。
2.每次访问从桶内取走一个令牌,当桶内令牌为0,则不允许再访问。
3.每隔一段时间,再放入令牌,最多使桶内令牌满额。(可以根据实际情况,每隔一段时间放入若干个令牌,或直接补满令牌桶)
我们可以使用redis的队列作为令牌桶容器使用,使用lPush(入队),rPop(出队),实现令牌加入与消耗的操作。
2.2代码实现
<?php
/**
* Class TokenBuKet 令牌桶算法
*/
class TokenBuKet
{
private $redis;
private $max; //令牌桶最大数量
private $queue; //令牌桶的名称
/**
* TokenBuKet constructor. //初始化
* @param $redis
* @param $max
* @param $queue
*/
public function __construct($redis,$max,$queue)
{
$this->redis=$redis;
$this->max=$max;
$this->queue=$queue;
}
/**
* @param int $num 添加令牌的数量S
*/
public function add($num=1){
$currentNum=intval($this->redis->llen($this->queue));
$maxNum=$this->max;
$num=$maxNum>=$currentNum+$num?$num:$maxNum-$currentNum;//最大令牌数>=当前令牌数+添加令牌?添加令牌:最大令牌-当前令牌
for($i=1;$i<=$num;$i++){
$this->redis->lpush($this->queue,$i);//把令牌放到队列里
}
}
/**
* 获取令牌
*/
public function get(){
return $this->redis->rPop($this->queue)?true:false;
}
/**
* 重置令牌
*/
public function reset(){
$this->redis->del($this->queue);//先删除这个key
$this->add($this->max);
}
}
$redis=new redis();
$redis->connect('127.0.0.1',6379);
$TokenBuKet=new TokenBuKet($redis,5,'token');
$TokenBuKet->reset();
for($i=0;$i<6;$i++){
var_dump($TokenBuKet->get());
}