本文目录导读:

- 第一层:网络层(Nginx/OpenResty)
- 第二层:应用前置层(PHP-FPM + 共享内存/Redis)
- 第三层:业务逻辑层(PHP + 数据库)
- 第四层:全局熔断层(PHP + Redis + 监控)
- 关键组合策略矩阵
- 调优与注意事项
- 最终建议
针对PHP项目的高并发防护,组合多维度限流规则的核心思路是:分层防御 + 动态调节,不同层级的限流关注不同的维度和异常流量特征。
以下是一个从底层到应用层的四层防护体系组合方案,每层使用不同的限流维度,配合对应的PHP实现策略。
第一层:网络层(Nginx/OpenResty)
目标:拦截IP、UA、Referer等基础特征,阻断大流量攻击。
-
维度:IP + User-Agent + 请求频率
-
规则组合:
- IP白名单/黑名单:CDN回源IP放行,爬虫IP封禁。
- 单IP QPS限制:
limit_req_zone $binary_remote_addr zone=perip:10m rate=30r/s; - 异常UA拦截:
if ($http_user_agent ~* (python|curl|wget)) { return 403; } - 请求方法校验:只允许
GET/POST,拒绝OPTIONS异常请求。
-
PHP代码介入点:通常不介入,但可通过
ngx_http_geoip_module传递国家代码给PHP做地理维度限流。
第二层:应用前置层(PHP-FPM + 共享内存/Redis)
目标:针对用户维度的精细限流,防止单一用户或设备耗尽资源。
-
维度:用户ID + 设备ID + 接口路径
-
规则组合:
-
滑动窗口计数器(Redis Sorted Set):
// 对用户API请求做限流 $key = "user_api_limit:{$userId}:{$endpoint}"; $now = microtime(true) * 1000; //毫秒 // 移除1秒前的记录 $redis->zRemRangeByScore($key, 0, $now - 1000); // 统计当前窗口内的请求数 $count = $redis->zCard($key); if ($count >= 5) { return ['code' => 429, 'msg' => '请求过于频繁']; } $redis->zAdd($key, $now, uniqid()); $redis->expire($key, 2); //设置过期 -
令牌桶(针对突发请求):
class TokenBucket { public function consume($userId, $rate = 10, $burst = 20) { $key = "token_bucket:{$userId}"; // Lua脚本保证原子性 $script = <<<LUA local bucket = redis.call('hgetall', KEYS[1]) local last_time = bucket[2] or ARGV[1] local tokens = bucket[4] or ARGV[2] local now = tonumber(ARGV[1]) local rate = tonumber(ARGV[3]) local burst = tonumber(ARGV[4]) local elapsed = math.max(0, now - last_time) tokens = math.min(burst, tokens + elapsed * rate) if tokens >= 1 then redis.call('hmset', KEYS[1], 'last_time', now, 'tokens', tokens - 1) return 1 else return 0 end LUA; return $redis->eval($script, [$key, $now, 10, $rate, $burst], 1); } } -
漏斗算法(漏桶):用于平滑QPS,适合第三方API调用控制。
-
第三层:业务逻辑层(PHP + 数据库)
目标:对具体业务资源(发帖、评论、登录)做差异化限流。
-
维度:操作类型 + 时间窗口 + 业务数据总量
-
规则组合:
- 按操作类型分级限流:
// 高频查询接口:10次/秒 // 登录接口:3次/分钟(同一IP+账号) // 发帖接口:1次/30秒(同一用户) $rateConfig = [ 'search' => ['window' => 1, 'limit' => 10], 'login' => ['window' => 60, 'limit' => 3], 'create_post'=> ['window' => 30, 'limit' => 1], ]; - 基于权重消耗:图片上传(权重5)、文本提交(权重1)。
- 突发流量惩罚:当单用户1s内请求超过阈值2倍时,全局降级(返回
503或者进入观察列表)。
- 按操作类型分级限流:
-
数据库级防护:针对刷库攻击,
INSERT操作前检查用户最近N秒内的写入次数。
第四层:全局熔断层(PHP + Redis + 监控)
目标:在分布式系统层面做自动调节,防止连锁故障。
-
维度:总体QPS + 慢查询比例 + 错误率
-
规则组合:
-
动态限流:根据服务器CPU、内存、请求排队数调整阈值。
-
熔断器模式:
class CircuitBreaker { const CLOSED = 0; const OPEN = 1; const HALF_OPEN = 2; public function isAvailable($apiName) { $redis = new Redis(); $key = "circuit:{$apiName}"; $state = $redis->get($key) ?? self::CLOSED; if ($state == self::OPEN) { $cooldown = $redis->ttl($key); if ($cooldown <= 0 && rand(0, 9) == 0) { // 10%几率尝试半开 return true; // 允许探针请求 } return false; } return true; } public function recordFailure($apiName) { $redis->incr("failure:{$apiName}"); $count = $redis->get("failure:{$apiName}"); if ($count >= 5) { // 连续5次失败 $redis->setex("circuit:{$apiName}", 30, self::OPEN); $redis->del("failure:{$apiName}"); } } } -
热点参数限流:对
SKU_ID、user_id等值做基于频率的热点保护。
-
关键组合策略矩阵
| 维度层 | 限流对象 | 存储介质 | 关键指标 | 生效速率 |
|---|---|---|---|---|
| 网络层 | IP/UA | Nginx共享内存 | 连接数 | 极速(毫秒) |
| 应用层 | 用户ID/设备ID | Redis | 请求次数 | 快速(毫秒级) |
| 业务层 | 操作类型/资源 | MySQL/Redis | 数据库写入量 | 中等(秒级) |
| 全局层 | 系统容量 | Redis/监控系统 | CPU/错误率 | 慢速(秒、分钟级) |
调优与注意事项
-
规则优先级:
- 先执行网络层/应用层的硬限制(阻断)。
- 再执行业务层的软限制(降级、排队、返回缓存数据)。
- 最后触发全局熔断时的紧急措施(全站只读/只服务核心VIP)。
-
缓存与降级协作:
- 当用户ID触发限流时,自动返回该ID的本地缓存(20s过期)。
- 当IP被限流时,直接返回CDN缓存的内容(静态化处理)。
-
动态降级策略:
// 根据当前系统负载动态调整 load($currentLoad) { if ($currentLoad > 70%) -> 削减非核心API的QPS阈值至50% if ($currentLoad > 90%) -> 关闭所有图片上传、评论等非核心接口 if ($currentLoad > 95%) -> 启动全局熔断,只返回静态页 } -
监控与日志:
- 每一层限流都需记录
user_id、ip、限流类型、触发值、拒绝次数。 - 使用
Prometheus + Grafana可视化限流指标,方便实时调整。
- 每一层限流都需记录
最终建议
PHP项目不要试图在应用层拦截秒级10k+的大流量,应该将网络层(Nginx)作为第一道墙,应用层只负责精细化用户级、业务级的限流,多层组合的核心是 各司其职 + 传递上下文(例如Nginx层标记可疑请求IP,PHP层对该IP提升限流等级)。