Laravel限流用令牌桶还是漏桶

wen PHP项目 14

本文目录导读:

Laravel限流用令牌桶还是漏桶

  1. 为什么 Laravel 选择令牌桶?
  2. 如何确认 Laravel 的实现?
  3. 什么时候需要考虑换成漏桶?
  4. 在 Laravel 中实现漏桶(如果需要)

在 Laravel 中,官方限流器 Illuminate\Cache\RateLimiter 底层使用的是令牌桶算法

具体原因和对比分析如下:

  • Laravel 内置限流器 → 令牌桶算法
  • Nginx 限流 → 漏桶算法limit_req 模块)
  • Redis + Lua 自定义 → 两种均可实现

为什么 Laravel 选择令牌桶?

令牌桶算法核心特性

  • 允许突发流量 (Burst):只要桶内有足够令牌,可以瞬间处理大量请求。
  • 平滑限流:令牌以固定速率补充,长期平均速率可控。

Laravel 的 RateLimiter 行为验证(代码逻辑):

// 以“每分钟 10 次”为例
RateLimiter::for('api', function ($job) {
    return Limit::perMinute(10);
});
  • 前 59 秒未请求:桶内积攒 10 个令牌。
  • 第 60 秒瞬间请求 10 次:全部放行(突发)。
  • 第 61 秒再次请求:被拒绝(桶已空,令牌未补充)。

这正是令牌桶的典型表现 —— 允许短时间内的突发流量。

漏桶算法的典型表现

  • 严格平滑:即使瞬间请求 100 次,也只以固定速率(如 10转/分钟)向外放行,多余的请求直接丢弃或排队。
  • 无法利用空闲时间:漏桶的“积水”恒定速度流出,不会因为之前空闲而允许更大的突发。

对比示例(同一场景):

时间点 请求数 令牌桶(Laravel) 漏桶
T=0 100 放行 10 次,拒绝 90 次 放行 10 次,拒绝 90 次
T=60(空闲 59 秒) 100 放行 100 次(桶积攒了 100 令牌) 放行 10 次,拒绝 90 次

Laravel 选择令牌桶就是因为 Web 应用需要容忍突发流量(如用户刷新、秒杀开始瞬间)。


如何确认 Laravel 的实现?

可以直接查看 Laravel 源码中 Illuminate\Cache\RateLimiterhit 方法:

public function hit($key, $decaySeconds = 60)
{
    $this->cache->add($key, 0, $decaySeconds);
    $this->cache->increment($key);
    // ... 还有 attempts() 方法读取当前次数
}
  • 它基于 Redis + 计数器实现。
  • 没有基于时间戳的严格“流出速率”控制,而是通过 固定时间窗口 + 计数重置decaySeconds)来模拟令牌的“补充”。
  • 这种实现本质上是一种 区间令牌桶(Fixed Window + Burst Allowed)。

什么时候需要考虑换成漏桶?

场景 推荐算法
API 网关、视频流、下载服务(要求绝对平滑,防止突刺) 漏桶
用户操作接口(如发帖、登录)(允许短暂突发,符合人类行为) 令牌桶(Laravel 默认)
秒杀系统、抢红包(需要精确控制瞬间流量,平滑流出) 漏桶 + 队列排队

在 Laravel 中实现漏桶(如果需要)

Laravel 没有内置漏桶,但可以借助 Redis + Lua 实现:

-- 漏桶 Lua 脚本
local key = KEYS[1]
local leak_rate = tonumber(ARGV[1])     -- 漏水速率 (请求/秒)
local capacity = tonumber(ARGV[2])      -- 桶容量
local now = tonumber(ARGV[3])
local water = redis.call('get', key)
if water == false then
    water = 0
    redis.call('set', key, 0)
end
-- 先漏水(根据时间差)
local leak_amount = (now - tonumber(redis.call('ttl', key))) * leak_rate
water = math.max(0, water - leak_amount)
if water < capacity then
    redis.call('set', key, water + 1)
    return 1  -- 放行
else
    return 0  -- 拒绝
end

但这样会丧失 Laravel 内置的优雅语法和 Redis 原子递增优势。


令牌桶(Laravel 默认) 漏桶
突发容忍 ✅ 允许 ❌ 严格平滑
实现复杂度 低(内置) 高(需自写 Lua)
适用场景 通用 Web API、用户操作 严格限流、流量整形
是否推荐 默认首选 ❌ 除非有明确平滑需求

建议:99% 的 Laravel 项目中,直接使用内置的 RateLimiter(令牌桶)即可,只有当你的业务逻辑明确要求“平滑输出”(如视频帧推流、实时数据管道)时,才考虑通过 Redis+Lua 或 Nginx 实现漏桶。

抱歉,评论功能暂时关闭!