Redis滑动窗口限流最可靠,因其原子操作、过期机制与单线程特性保障精确计数、自动清理和无竞态;需用Lua脚本保证incr+expire原子性,ZSET实现滑动窗口,多级键设计,可信代理头获取真实IP。
用
实现滑动窗口限流最可靠
PHP 自身没有原生限流能力,靠
或文件锁既不准又难扩展。生产环境必须用外部存储,
是事实标准——它支持原子操作、过期键和
/
组合,能精准实现滑动窗口(如“每分钟最多 60 次”)。别用 MySQL 记录请求时间戳,高并发下写入瓶颈和事务开销会直接拖垮接口。
实操建议:
用
批量执行
和
,避免两次网络往返
窗口粒度设为秒级(如 1 秒内最多 5 次),再用前端聚合判断“每分钟”,比直接存分钟级 key 更平滑
key 命名用
,避免不同用户或接口互相干扰
注意
连接池配置,短连接在限流场景下极易打满连接数
不足以识别真实客户端
只要经过 Nginx、CDN 或云 WAF,
就是代理 IP,所有用户会被判成同一个来源,导致误封或失效。必须逐层检查
、
等头,并只取最左一个非私有地址。
常见错误现象:
立即学习
“
PHP免费学习笔记(深入)
”;
本地开发正常,上线后全站被限流——因为 CDN 回源 IP 全是内网段(如
)
同一办公室用户集体触发限流——所有请求都带相同
值
安全做法:
PHP 8.5.5
PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。
下载
在 Nginx 配置中用
明确可信代理段,再启用
PHP 中用
过滤私有/保留地址
必要时叠加
或设备指纹(如 TLS 指纹哈希)做辅助识别
返回
时必须带标准响应头
光返回状态码不够。前端或网关依赖
、
、
这三个头做退避策略。缺失它们,调用方无法判断何时重试,容易陷入死循环重发。
参数差异与性能影响:
应填 Unix 时间戳(秒级),不是毫秒——很多 SDK 会直接
不要在每次请求都查 Redis 获取剩余次数:用 pipeline 一次读多个 key,或把计数缓存在 PHP-FPM 进程内(仅适用于单机且窗口较长的场景)
若用 Swoole,可将计数存在
,避免 Redis 往返,但需自行处理进程间同步和过期
白名单和突发流量要分开处理
运维、内部系统、支付回调等必须绕过限流,但不能硬编码在逻辑里。应从配置中心或数据库动态加载白名单规则,否则每次变更都要重启服务。同样,活动期间临时放宽限制(如双 11 提升到每秒 200 次),也不能改代码。
实操要点:
白名单匹配优先级:API 路径 > 用户 Token > IP 段 > User-Agent 前缀(如
)
用
控制“突发配额”开关,比如设置
过期 30 分钟,命中即走宽松策略
记录被限流的请求到
或 Kafka,字段至少含 IP、路径、User-Agent、触发阈值——没日志就等于没防护
真正难的是权衡:太严伤体验,太松挡不住爬虫。IP + 接口 + 时间窗口三元组是底线,其他维度(如请求体特征、行为序列)得用专门的风控服务,PHP 层只做第一道过滤。
Redissleep()RedisINCREXPIRERedis::pipeline()INCREXPIRE"rate_limit:{$ip}:{$api_path}"Redis$_SERVER['REMOTE_ADDR']$_SERVER['REMOTE_ADDR']HTTP_X_FORWARDED_FORHTTP_X_REAL_IP10.0.0.0/8X-Forwarded-Forset_real_ip_fromreal_ip_header X-Forwarded-Forfilter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)$_SERVER['HTTP_USER_AGENT']429 Too Many RequestsX-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-ResetX-RateLimit-Resetnew Date(reset * 1000)Swoole\Tableinternal-bot/Redis SETNX"burst_mode:orders"syslog