直接用 Laravel 自带的 throttle 中间件,它已支持 Redis、数据库后备、动态 key 生成和防穿透;自行实现易出现并发竞争、时间窗口漂移、跨服务器不一致等问题。

限流中间件该用 throttle 还是自己写?
直接用 Laravel 自带的 throttle 中间件,别手撸——它已支持 Redis、数据库后备、动态 key 生成,且默认防穿透(比如带随机参数的请求不会绕过计数)。自己实现容易漏掉并发竞争、时间窗口漂移、跨服务器不一致这些坑。
常见错误现象:ThrottleRequestsException 频繁抛出但日志没记录;或限流在本地开发正常,上线后完全失效(Redis 配置未生效)。
- 确认
CACHE_DRIVER和REDIS_CLIENT在生产环境指向真实 Redis,而非array或file - 检查
config/cache.php中default是否与throttle实际使用的 store 一致(它默认走cachestore,不是redis) - 若需按用户角色差异化限流(如 VIP 每分钟 500 次),不要改中间件源码,用闭包式 key:
throttle:60,500,reset_in=3600,by=id+ 自定义resolveKey
API 路由限流时怎么区分「登录用户」和「游客」?
靠 Request 对象本身判断,而不是在中间件里硬写 Auth::id()——后者在 API 路由中可能因 guard 未切换而返回 null。
使用场景:游客限流 10 次/分钟,登录用户 100 次/分钟,JWT 和 session 登录都要兼容。
- 在路由定义里用闭包语法:
middleware('throttle:10,1|100,1,by=throttle_key') - 在
App\Http\Middleware\EnsureThrottleKeyIsSet.php中动态设置:$request->attributes->set('throttle_key', $request->user() ? 'user_'.$request->user()->id : 'guest_'.($request->ip() ?: 'unknown')) - 避免用
session_id()区分游客——无状态 API 下 session 可能未启动,且移动端 IP 复用率高,易误杀
throttle 中的 reset_in 参数到底影响什么?
它只控制 HTTP 响应头 X-RateLimit-Reset 的值,**不影响实际限流逻辑**。真正决定窗口期的是第二个参数(如 throttle:60,100 中的 100)背后的存储策略:Laravel 默认用「滑动窗口」(基于 Redis ZSET),reset_in 不参与计算。
性能影响:设过大的 reset_in(比如 reset_in=86400)会让前端误以为“今天彻底禁了”,但后端其实每分钟都在滚动统计——这会造成体验断层。
- 建议保持
reset_in与速率单位一致:throttle:60,100,reset_in=60 - 不要依赖它做业务逻辑分支(比如“重置后发短信”),要用独立定时任务清理 Redis key 或监听
RateLimitingAttempted事件 - 如果真需要固定窗口(如“每天最多 5 次”),得换方案:
throttle:1,5,by=uid,window=86400不行,得用自定义中间件 +Cache::add("rate_limit:uid:{$uid}", 1, 86400)
为什么加了 throttle 但 Postman 测试一直 200?
大概率是当前请求没命中你写的那个中间件规则——Laravel 的 throttle 是按路由组或单个路由绑定的,不是全局生效。另外,测试时用的账号可能被缓存或共享了限流 key。
排查步骤:
- 运行
php artisan route:list --middleware=throttle,确认目标路由确实绑定了throttle - 在中间件里临时加日志:
Log::debug('throttle key: '.app(\Illuminate\Routing\Router::class)->getRateLimiter()->getKey($request, 'api')) - Postman 测试时关掉所有 Cookie,清空 Header 中的
Authorization,排除 token 复用导致 key 相同 - 检查是否启用了
APP_DEBUG=false却没配好LOG_CHANNEL=stack,导致日志根本没写入
最容易被忽略的一点:Laravel 10+ 默认把 API 路由放在 routes/api.php,而这个文件里注册的路由自动加了 api 中间件组——如果你在 app/Http/Kernel.php 里把 throttle:api 写进了 web 组,那对 API 根本不生效。










