php限流核心在于redis+lua实现令牌桶算法,在请求入口处按ip或用户维度拦截。令牌桶可应对短时突发且控长期速率,适合登录、短信等接口;需避免固定窗口临界突增问题,强一致性场景推荐漏桶。

PHP框架做限流,核心不是“能不能拦”,而是“在哪拦、按什么维度拦、怎么拦得稳”。接口被刷崩往往发生在登录、短信、验证码等高频敏感路径,单靠数据库或内存计数根本扛不住并发——必须用Redis+原子操作,在请求刚进入口时就完成判断。
选对算法:令牌桶比固定窗口更适合真实业务
固定窗口(比如“1分钟最多10次”)有临界突增问题:前59秒没调用,第60秒一口气来10次,下一秒又来10次,实际峰值是20次/秒;而令牌桶允许合理突发,同时控住长期速率。例如设置桶容量100、填充速率20/s,既能应对短时批量操作(如前端重试),又不会让后端持续过载。
- 适合用户登录、发短信等需兼顾体验与安全的接口
- 不推荐用于支付回调、库存扣减等强一致性场景(此时漏桶更稳)
- 键名设计要带维度标识,比如rate:token:api/login:{ip}或rate:token:api/sms:{user_id}
用好Redis:Lua脚本是并发安全的唯一解
PHP本身没有原子操作能力,靠INCR+EXPIRE两步走,高并发下必然丢数据。必须把“计算新增令牌、更新时间戳、判断是否放行、消耗令牌”整个逻辑打包成一段Lua脚本,由Redis单线程执行。
- 脚本里用
redis.call('time')获取服务端时间,避免客户端时间不准 - 桶容量和速率写死在脚本里,或通过
ARGV传入,方便复用 - 返回值统一为数字:1=放行,0=拒绝,-1=系统错误(如Redis不可用)
ThinkPHP8实战:中间件+自研令牌桶双保险
官方think-throttle够快但不够灵活,尤其在用户未登录时 fallback 到 IP 容易被绕过。建议路由层用它做兜底,关键接口再加一层自研令牌桶中间件:
- 中间件中用
Container::getInstance()->get('cache')->handler()拿原生Redis实例,避开Cache门面封装导致的pipeline失效 - IP提取优先读
X-Real-IP头(Nginx透传),再 fallback 到$request->ip() - 拒绝请求时返回
json(['code'=>429, 'msg'=>'请求过于频繁'])并设HeaderRetry-After: 60
防刷不止限流:配合参数校验与空值缓存
单纯限流挡不住构造无效参数的扫库攻击。比如用不存在的手机号反复调/api/sms/send,即使每分钟只放行5次,也能持续穿透到数据库。
- 在限流前先校验手机号格式、是否已注册(查缓存而非DB)
- 对查无此用户的请求,也写入
black:user:138****1234并设30秒TTL,后续直接拦截 - 结合Nginx层
limit_req做第一道网关过滤,减轻PHP进程压力
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











