Laravel 10:ThrottleRequest结合缓存实现限流异常排查
API限流计数未重置问题修复
你要实现的是:正式环境60分钟内允许100次请求,测试环境5秒内10次,超时后计数重置为0,但现有代码在5秒后仍限制请求,核心问题出在计数存储和逻辑流程上,具体问题及修复方案如下:
现有代码的问题
- 计数存储错误:把请求次数存在数据库的
ignore_limits字段里,这是持久化存储,不会自动重置。即使缓存超时后把变量$requests设为0,后续又会从数据库读取旧值覆盖,导致重置无效。 - 逻辑顺序混乱:超时分支重置
$requests后,else分支又将其赋值为数据库的ignore_limits值,等于白重置了。 - 调试语句阻断执行:
dd(Cache::has($key));会直接中断代码,后续的缓存更新、数据库保存都不会执行。
修复后的代码
$key = $request->apikey; $apiKey = ApiKey::where('key', $key)->first(); if (!$apiKey) { return response()->json(['message' => '无效的API密钥'], 401); } // 根据环境自动切换限流规则 if (app()->environment('production')) { $rateLimit = 100; $timer = 3600; // 60分钟=3600秒 } else { $rateLimit = 10; $timer = 5; // 测试环境5秒 } // 定义规范的缓存键名 $countCacheKey = "api_rate:{$key}"; $timerCacheKey = "api_rate_timer:{$key}"; // 从缓存获取当前计数和上次请求时间,默认0 $requests = Cache::get($countCacheKey, 0); $lastRequestTime = Cache::get($timerCacheKey, 0); // 超时则重置计数 if (time() - $lastRequestTime > $timer) { $requests = 0; } // 检查是否超限流 if ($requests >= $rateLimit) { return response()->json(['message' => '请求次数超过限制'], 429); } // 更新缓存:计数+1,同时设置过期时间 Cache::put($countCacheKey, $requests + 1, now()->addSeconds($timer)); Cache::put($timerCacheKey, time(), now()->addSeconds($timer)); return $next($request);
修复说明
- 改用缓存存储计数:缓存是临时存储,既可以自动过期,也能在超时后主动重置,完全符合限流的临时计数需求,不需要再操作数据库的
ignore_limits字段(如果这个字段没用可以直接删掉)。 - 调整逻辑顺序:先判断超时重置计数,再检查是否超限流,流程更清晰。
- 移除调试语句:确保代码能完整执行。
- 环境自动适配:根据应用环境自动切换正式/测试的限流参数,不用手动修改。
内容的提问来源于stack exchange,提问作者Kyuzzero
相关产品推荐
相关产品推荐

