You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 13:25:19