必须根据下游承载力、流量特征、业务容忍度选择漏桶或令牌桶:保护脆弱下游用漏桶,扛自身突发流量用令牌桶,混合场景需入口令牌桶+出口漏桶分层限流。

在Laravel项目中做接口限流时,必须明确:不是选“看起来更稳”的算法,而是根据下游承载力、流量特征、业务容忍度来决定令牌桶或漏桶——比如支付回调接口被第三方银行限频,就必须用漏桶;而秒杀预热接口要扛住用户集中点击,就得靠令牌桶攒出的突发余量。
先看下游是否脆弱:保护别人优先用漏桶
第一步:确认你调用的外部服务是否有硬性速率上限,例如银行接口TPS≤30、短信网关QPS≤50、微信OAuth2接口每分钟1000次。这类服务一旦超限会直接返回429或封IP,且无重试缓冲机制。
第二步:检查该服务的错误响应是否具备可识别特征(如HTTP状态码429、响应体含"rate_limit_exceeded")。若无法精准识别拒绝信号,漏桶就失去兜底意义——【漏桶生效的前提是能准确知道“谁在溢出”】。
第三步:在Laravel中用中间件封装漏桶逻辑,桶容量设为下游阈值的80%,流出速率严格等于下游允许的最大速率(如银行TPS=30,则rate=30/s)。不设缓冲队列,溢出请求直接返回429并附带Retry-After头。
再看自身是否扛得住:保护自己优先用令牌桶
方法一:用 Laravel RateLimiter + Redis 实现令牌桶
在app/Providers/AppServiceProvider.php的boot()方法中注册限流规则:
RateLimiter::for('api-burst', function (Request $request) { return Limit::perMinute(600)->by($request->ip())->response(function () { return response(['message' => '请求过于频繁'], 429); }); });
这本质是固定窗口计数器,不是令牌桶——别被命名误导。真正令牌桶需手动实现。
方法二:集成 guava-ratelimiter 的 PHP 移植版(如 php-rate-limiter)
安装后在中间件中初始化:$bucket = new TokenBucket(100, 20); // 容量100,每秒补20个令牌。每次请求调用$bucket->tryAcquire(),返回true才放行。
【桶容量不能超过单机PHP-FPM进程最大并发数】,否则令牌虽有但Worker已满,请求仍在排队阻塞。
混合场景:用漏桶控出口 + 令牌桶控入口
典型场景:Laravel作为API网关,上游是用户App(流量突发),下游是自研订单服务(需平滑处理)。
① 入口层用令牌桶:控制App端每IP每分钟最多获取600个令牌,允许短时脉冲(如开抢瞬间涌进800请求,前600秒内放行,后200秒内拒绝)。
② 出口层用漏桶:对订单服务调用强制限速为50 QPS,无论入口令牌多寡,发往订单服务的请求必须以恒定50/s速率发出——避免压垮数据库连接池。
③ 两个桶独立计数,共享同一Redis实例,但key前缀分离(如token:ip:{hash} vs leak:order:{service})。
这一步做完后,Laravel项目中的限流策略已按实际依赖关系完成分层部署。


















