有时候我们需要限制一个api或页面访问的频率,例如单ip或单用户一小时之内只能访问20次。
类似于这样的需求可以用Redis来高效地实现。
新建文件 IPRestrict.php
<?php /** * PHP实现redis限制单ip、单用户的访问次数功能 * Created by www.guihet.com. **/ class IPRestrict { /** * @var Redis */ private static $redis; /** * @var string * 取客户端真实ip地址作为key */ private static $realip; public function __construct($host = '127.0.0.1', $port = 6379, $auth = null) { self::$redis = new Redis(); self::$redis->connect('127.0.0.1', 6379); $auth && self::$redis->auth($auth); self::$realip = self::getRealIP(); } /** * 取得客户端IP地址 * @return string */ public static function getRealIP() { $realip = ""; if (isset($_SERVER)) { foreach(['HTTP_X_FORWARED_FOR', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'] as $name) { if (isset($_SERVER[$name])) { $realip = $_SERVER[$name]; break; } } } else { $realip = getenv('HTTP_X_FORWARDED_FOR') || getenv('HTTP_CLIENT_IP') || getenv('REMOTE_ADDR'); } return $realip; } /** * 记录该ip的访问次数 也可改成用户id * @param int $limit 限制指定时间内的访问磁珠 * @param int $time 限制时间为60秒 * @return int * @throws HttpException */ public function requestCount($limit = 20, $time = 60*60) { $key = self::$realip; $redis = self::$redis; /** * @var bool */ $exists = $redis->exists($key); $redis->incr($key); if ($exists) { $count = $redis->get($key); if ($count > $limit) { throw new Exception('请求太频繁,请稍后再试!'.$count); } } else { // 首次计数 设定过期时间 $redis->expire($key, $time); } return $redis->get($key); } } ?>
新建测试文件 ip.php
<?php include 'IPRestrict.php'; $restrict = new IPRestrict(); echo $restrict->requestCount(); ?>
这样访问 ip.php 就输出访问次数。
为了更加准确的获取访问用户 ip,可以使用以下函数。
<?php /** * 获取客户端IP地址 * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字 * @param boolean $adv 是否进行高级模式获取(有可能被伪装) * @return mixed */ function get_client_ip($type = 0,$adv=false) { $type = $type ? 1 : 0; static $ip = NULL; if ($ip !== NULL) return $ip[$type]; if($adv){ if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $pos = array_search('unknown',$arr); if(false !== $pos) unset($arr[$pos]); $ip = trim($arr[0]); }elseif (isset($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; }elseif (isset($_SERVER['REMOTE_ADDR'])) { $ip = $_SERVER['REMOTE_ADDR']; } }elseif (isset($_SERVER['REMOTE_ADDR'])) { $ip = $_SERVER['REMOTE_ADDR']; } // IP地址合法验证 $long = sprintf("%u",ip2long($ip)); $ip = $long ? array($ip, $long) : array('0.0.0.0', 0); return $ip[$type]; } ?>
调用 get_client_ip(0, true);
即可
覅覅覅覅覅覅覅覅覅覅覅覅覅覅覅覅覅