php实现ip限流
PHP实现IP限流的方法
使用Redis实现令牌桶算法
Redis的原子操作和高效性适合实现令牌桶算法。以下是一个基于Redis的PHP实现示例:
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$ip = $_SERVER['REMOTE_ADDR'];
$key = "rate_limit:$ip";
$capacity = 10; // 桶容量
$rate = 1; // 每秒添加的令牌数
$now = microtime(true);
$redis->watch($key);
$data = $redis->hGetAll($key);
$tokens = isset($data['tokens']) ? $data['tokens'] : $capacity;
$lastTime = isset($data['last_time']) ? $data['last_time'] : $now;
$delta = max(0, $now - $lastTime);
$newTokens = min($capacity, $tokens + $delta * $rate);
if ($newTokens < 1) {
$redis->unwatch();
header('HTTP/1.1 429 Too Many Requests');
exit;
}
$redis->multi();
$redis->hSet($key, 'tokens', $newTokens - 1);
$redis->hSet($key, 'last_time', $now);
$redis->expire($key, 3600);
$redis->exec();
使用文件系统实现简单计数
对于小型应用,可以使用文件系统记录IP访问次数:
$ip = $_SERVER['REMOTE_ADDR'];
$limit = 100; // 每小时限制
$file = "ratelimit_$ip.txt";
$current = file_exists($file) ? (int)file_get_contents($file) : 0;
if ($current >= $limit) {
header('HTTP/1.1 429 Too Many Requests');
exit;
}
file_put_contents($file, $current + 1);
使用数据库存储访问记录
MySQL数据库方案适合需要持久化存储的场景:
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$ip = $_SERVER['REMOTE_ADDR'];
$window = 60; // 60秒窗口
$limit = 30; // 30次请求
$stmt = $pdo->prepare("SELECT COUNT(*) FROM access_log
WHERE ip = ? AND timestamp > DATE_SUB(NOW(), INTERVAL ? SECOND)");
$stmt->execute([$ip, $window]);
$count = $stmt->fetchColumn();
if ($count >= $limit) {
header('HTTP/1.1 429 Too Many Requests');
exit;
}
$pdo->prepare("INSERT INTO access_log (ip, timestamp) VALUES (?, NOW())")->execute([$ip]);
使用APCu内存缓存
APCu提供快速的进程内缓存,适合单服务器环境:
if (!extension_loaded('apcu')) {
die('APCu extension required');
}
$ip = $_SERVER['REMOTE_ADDR'];
$key = "rl_$ip";
$limit = 50; // 每分钟50次
$window = 60; // 60秒
$count = apcu_fetch($key, $success);
if (!$success) {
apcu_store($key, 1, $window);
} elseif ($count < $limit) {
apcu_inc($key);
} else {
header('HTTP/1.1 429 Too Many Requests');
exit;
}
Nginx层限流配置
在Web服务器层面实现限流更高效,Nginx配置示例:
http {
limit_req_zone $binary_remote_addr zone=ip_limit:10m rate=10r/s;
server {
location / {
limit_req zone=ip_limit burst=20 nodelay;
}
}
}
响应头返回限流信息
良好的API设计应包含限流信息在响应头中:

header('X-RateLimit-Limit: 100');
header('X-RateLimit-Remaining: '.(100 - $count));
header('X-RateLimit-Reset: '.$resetTime);
选择具体方案时应考虑应用规模、性能要求和基础设施。分布式系统需要Redis或专业API网关,小型应用可采用文件或数据库方案。






