redis实现限流

redis实现限流(list数据结构和令牌桶算法)

1.基于Redis的数据结构list

1.1实现流程

我们可以将请求打造成一个list列表,当每一次请求进来的时候,value用当前时间戳表示。可以清楚判断一段时间的访问次数是否超过限制。

image-20210318154003930

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());
}


猜你喜欢

转载自blog.csdn.net/weixin_49298265/article/details/115049527