zoukankan      html  css  js  c++  java
  • PHP结合Redis来限制用户或者IP某个时间段内访问的次数【附加延伸】两种不同类型方式

    我们知道当网站的访问量突然很大的时候肯定会对服务器造成影响,甚至无法访问,如果是正常的访问那么很好说明业务量增大可以考虑系统的扩展,但是如果是搜索引擎爬虫频繁访问或是一些恶意访问,那这时候我们就应该限制这些访问的访问次数。redis刚好可以解决这个问题

    一、方式一

    限制每个用户每分钟最多只能访问100个页面。实现思路:key使用有"rate.limiting:IP",value使用数值,用户每次访问将value的值通过INCR命令自增1.如果自增后的值是1同时设置过期时间为1分钟。这样用户每次访问的时候都读取该键的值,如果超过了100就表明该用户的访问频率超过了限制,需要提示用户稍后访问。且该键每分钟会自动被删除。所以下一分钟又会重新计算,也就达到了限制访问频率的目的。

     代码:

    String key = "rage.limiting:"+ip;
    // 判断key是否存在
    int flag = exists(key);// key rate.limiting:192.168.88.60
    if(flag == 1){
        // key 存在 自增1
        int count = incr(key);
        if(count > 100){
            // 超过限制
            log.info("访问频率超过了限制,请稍后重试");
            return ;
        }
    }else{
        // key 不存在 
        multi(); // 开启事务
        incr(key); // key不存在自增1 值为1
        expire(key,60); // 设置过期时间
        exec(); // 提交事务
    }

    二、方式二

    实现方式一其实还有个问题,比如如果用户第一分钟的访问了99次,前面58秒访问了9次,后面1秒访问了90次,然后用户后一秒也访问了99次,而后一分钟的第一秒访问了90次,后面的58秒访问了9次,这样按照上面的算法是没有问题的,但是这种极端情况大家还是可以发现问题的。

    解决方法:先将上面案例中的100次调整为10次便于在次场景中描述,要精确的保证同一个用户每分钟最多访问10次,需要记录下来用户每次访问的时间。因此对每个用户我们使用一个List列表类型的键来记录他最近10次访问的时间,一旦键中的元素超过10个,就判断最早的元素距离现在的时间是否小于1分钟。如果是表示用户最近1分钟访问次数超过了10次,如果不是就将现在的时间加入到队列中,同时把最早的元素删除。

     代码

    String key = "rate.limiting:"+IP;
    int listLength = llen(key);
    if(listLength < 10){
        lpush(key,new());
    }else{
        long time = lindex(key,-1);
        if(now()-time < 60){
            log.info("访问频率超过了限制,请稍后再试");
        }else{
            lpush(key,now);
            ltrim(key,0,9);
        }
    }

    PHP代码(存在不足,结合上面案例更改)

    $redis = new Redis();    
    $redis->connect('127.0.0.1', 6379);   
    
    //获取客户端真实ip地址  
    function get_real_ip(){  
        static $realip;  
        if(isset($_SERVER)){  
            if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){  
                $realip=$_SERVER['HTTP_X_FORWARDED_FOR'];  
            }else if(isset($_SERVER['HTTP_CLIENT_IP'])){  
                $realip=$_SERVER['HTTP_CLIENT_IP'];  
            }else{  
                $realip=$_SERVER['REMOTE_ADDR'];  
            }  
        }else{  
            if(getenv('HTTP_X_FORWARDED_FOR')){  
                $realip=getenv('HTTP_X_FORWARDED_FOR');  
            }else if(getenv('HTTP_CLIENT_IP')){  
                $realip=getenv('HTTP_CLIENT_IP');  
            }else{  
                $realip=getenv('REMOTE_ADDR');  
            }  
        }  
        return $realip;  
    }  
    
    //这个key记录该ip的访问次数 也可改成用户id   
    $key = get_client_ip();  //该Key记录访问的次数,目前是以IP为例,也可以把用户id作为key,如userid_123456
      
    //限制次数为3次。  
    $limit = 3;  
      
    $check = $redis->exists($key);  
    if($check){  
        $redis->incr($key);  
        $count = $redis->get($key);  
        if($count > 3){  
            exit('已经超出了限制次数');  
        }  
    }else{  
        $redis->incr($key);  
        //限制时间为60秒   
        $redis->expire($key,60);  
    }  
      
    $count = $redis->get($key);  
    echo ''.$count.' 次请求';

     

     摘自:https://www.cnblogs.com/dengpengbo/p/10545787.html

  • 相关阅读:
    MySQL的数据库时间与电脑系统时间不一致
    csv文件处理——Opencsv
    java中的注解大全@controller、@service、@repository等
    浅谈HTTP中Get、Post、Put与Delete的区别
    plsql导入.dmp步骤
    解决maven官方库中没有oracle jdbc驱动的问题:Missing artifact com.oracle:ojdbc14:jar:10.2.0.1.0
    String的拼接
    使用java中汇编指令解析String对象(String s1="a";String s2=s1+"b";String s3=new String("a");String s4=s3+"a";)
    集合 List ,Set
    关于==和equals的区别和联系,面试这么回答就可以
  • 原文地址:https://www.cnblogs.com/wt645631686/p/6868845.html
Copyright © 2011-2022 走看看