Redis achieves current limit

Redis implements current limiting (list data structure and token bucket algorithm)

1. Data structure list based on Redis

1.1 Implementation process

We can make the request into a list, and every time a request comes in, the value is represented by the current timestamp. It can be clearly judged whether the number of visits in a period of time exceeds the limit.

image-20210318154003930

2. Code implementation
<?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. Token bucket algorithm based on Redis

2.1 Implementation process

1. First, set up a token bucket in which tokens are stored. At the beginning, the tokens in the token bucket are full (the number of tokens in the bucket can be set according to the situation of the server).

2. One token is taken from the bucket for each access. When the token in the bucket is 0, no more access is allowed.

3. At regular intervals, put tokens in again to make the bucket full of tokens at most. (According to the actual situation, you can put in several tokens at regular intervals, or directly fill the token bucket)

We can use the redis queue as a token bucket container, and use lPush (enter the queue) and rPop (dequeue) to implement the operation of token addition and consumption.

2.2 Code implementation
<?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());
}


Guess you like

Origin blog.csdn.net/weixin_49298265/article/details/115049527