Laravel基于认证用户TPS限流异常及剩余请求次数获取咨询
问题解决:Laravel限流未达次数触发429及剩余请求次数获取
一、未达最大请求次数触发429的排查与修复
1. 分布式环境计数不一致
如果应用部署在多服务器集群,默认文件/数据库缓存驱动会导致每个服务器独立维护限流计数,可能出现单服务器配额耗尽但总请求数未达上限的情况。
修复方案:
切换缓存驱动为Redis(分布式共享存储),修改config/cache.php的默认配置:
'default' => env('CACHE_DRIVER', 'redis'),
2. TPS值类型或计算错误
代码中$tps * 60可能因$user->tps为非数字值(如字符串、null),计算出异常限流上限(比如0),导致提前触发限制。
修复方案:
强制校验并转换TPS为整数,确保计算准确:
// 替换原$tps赋值逻辑 $tps = is_numeric($user->tps) ? (int)$user->tps : config('api.DEFAULT_TPS'); $maxAttempts = $tps * 60;
3. 固定窗口限流的突发请求误解
当前使用perMinute($tps*60)是固定窗口限流,允许用户在窗口内任意时间耗尽所有请求配额(比如TPS=1,每分钟60次,用户可在1秒内发完60次,后续59秒都被限制),这种情况会让用户误以为未达次数就被拦截。
修复方案:
改用令牌桶算法实现精准TPS控制,替换perMinute为perSecond:
// 替换原Limit定义 return Limit::perSecond($tps)->by($key)->response(function (Request $request, array $headers) use ($user, $key) { // 保持原回调逻辑不变 });
4. 限流Key冲突
需确保user_id无重复,或未被误用于其他限流规则。
验证方法:
在日志中打印完整的$key值,确认每次请求的Key与用户身份匹配:
// 在hit日志中添加key明文打印 Log::channel('request-log')->info("Request hit, key: {$key}, tps: {$tps}");
二、获取用户剩余请求次数
1. 全局响应头返回剩余次数
创建自定义中间件,在所有API响应中添加限流相关头信息:
// app/Http/Middleware/AddRateLimitHeaders.php <?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\RateLimiter; class AddRateLimitHeaders { public function handle(Request $request, Closure $next) { $response = $next($request); $user = $request->user(); if ($user && $user->is_api) { $tps = is_numeric($user->tps) ? (int)$user->tps : config('api.DEFAULT_TPS'); $maxAttempts = $tps * 60; $key = $user->id; } else { $maxAttempts = 60; $key = optional($user)->id ?: $request->ip(); } $remaining = RateLimiter::remaining($key, $maxAttempts); $resetTimestamp = now()->addSeconds(RateLimiter::availableIn($key))->getTimestamp(); $response->headers->add([ 'X-RateLimit-Limit' => $maxAttempts, 'X-RateLimit-Remaining' => $remaining, 'X-RateLimit-Reset' => $resetTimestamp, ]); return $response; } }
注册中间件到app/Http/Kernel.php的api中间件组:
protected $middlewareGroups = [ 'api' => [ // 其他中间件... \App\Http\Middleware\AddRateLimitHeaders::class, ], ];
2. 直接在业务逻辑中获取
在控制器或业务代码中,直接调用RateLimiter方法获取剩余次数:
use Illuminate\Support\Facades\RateLimiter; // 获取当前用户的限流Key和上限 $user = auth()->user(); $key = $user->is_api ? $user->id : ($user->id ?? request()->ip()); $maxAttempts = $user->is_api ? ((int)$user->tps * 60) : 60; $remainingAttempts = RateLimiter::remaining($key, $maxAttempts); $availableInSeconds = RateLimiter::availableIn($key);
3. 限流触发时获取剩余次数
当前代码的429回调中已经通过$headers['X-RateLimit-Remaining']获取剩余次数,该值由Laravel自动计算,可直接用于日志或返回给前端。
内容的提问来源于stack exchange,提问作者dg9495
相关产品推荐
相关产品推荐

