漏斗算法(Funnel Algorithm

漏斗算法(Funnel Algorithm)

一、核心原理(图示+说明)

+-----------------------+
|                       |
|      待处理请求       |  ← 新请求从顶部注入
|      (水量)         |
|                       |
+-----------------------+
|       漏嘴            |  ← 按固定速率(leakRate)流出请求
+-----------------------+
    ↑
    超出容量的请求从顶部溢出(被拒绝)

(示意图说明:漏斗上宽下窄,顶部接收请求,底部以固定速率漏水,超出容量的请求从顶部溢出)

  • 核心思想:模拟漏斗盛水→漏水的过程,控制请求流量:
    • 顶部:接收新请求(像水流注入漏斗)。
    • 容量:漏斗最多能装的水量(最大积压请求数)。
    • 漏嘴:底部固定大小的出口,以固定速率漏水(单位时间允许通过的请求数)。
    • 溢出:当注入速度>漏水速度,水满后多余的水溢出(请求被拒绝)。

二、核心参数(4个关键指标)

参数名 含义 示例值
capacity 漏斗总容量(最大可积压请求数) 100(个)
leakRate 漏嘴速率(单位时间允许通过的请求数) 10(个/秒)
water 当前水量(当前积压的请求数) 30(个)
lastTime 上次更新水量的时间戳(秒) 1620000000

三、执行流程(动态调整逻辑),每一次请求都要执行

每次请求到来时,按以下步骤处理(结合图示理解):

  1. 计算时间差

    上次更新时间 →——————— deltaTime ———————→ 当前时间
    

    (当前时间 - 上次更新时间 = deltaTime,即两次请求的间隔)

  2. 计算漏水量
    这段时间内漏嘴漏掉的水量:
    漏水量 = deltaTime × leakRate
    (例:间隔3秒,漏嘴速率10个/秒 → 漏水量=3×10=30)

  3. 更新当前水量
    水量随时间减少,且不能为负:
    water = max(0, 旧water - 漏水量)
    (例:旧water=50,漏水量=30 → 新water=20)

  4. 判断是否允许请求

    +-----------------------+
    |  当前水量:20          |
    |                       |  → 新请求到来,判断 20+1 ≤ 100?
    +-----------------------+     → 是 → 允许(水量变为21)
                                  → 否 → 拒绝
    
    • water + 1 ≤ capacity:允许请求(water+1,更新lastTime)。
    • 否则:拒绝请求(漏斗已满,溢出)。

四、优缺点与适用场景

维度 内容
优点 1. 长期流量严格限制在漏嘴速率内;
2. 逻辑简单,易实现。
缺点 1. 高并发下需频繁计算时间差,对性能有要求;
2. 分布式场景需中间件保证一致性。
适用场景 接口限流、秒杀流量控制、用户行为频率限制(如评论、登录)等。

五、分布式实现关键点(Redis+Lua)

  1. 原子性保证:用Lua脚本封装整个流程,避免并发修改导致的水量误差。
  2. 状态存储:Redis的Hash结构存储4个核心参数(water、lastTime等)。
  3. 性能优化:连接池复用Redis连接、本地限流前置过滤请求、Redis集群分流。

六、具体的Lua脚本

-- 漏斗限流脚本:key为限流对象(如"user:123"),返回1表示允许,0表示拒绝
local key = KEYS[1]
local capacity = tonumber(ARGV[1])  -- 漏斗容量
local leakRate = tonumber(ARGV[2])  -- 漏嘴速率(请求/秒)

-- 从Redis获取当前漏斗状态(不存在则初始化)
local current = redis.call("HMGET", key, "water", "lastTime")
local water = tonumber(current[1] or 0)
local lastTime = tonumber(current[2] or 0)
local now = tonumber(redis.call("TIME")[1])  -- 当前时间戳(秒)

-- 计算漏水:deltaTime秒内漏掉的水量
local deltaTime = now - lastTime
local leakWater = deltaTime * leakRate
water = math.max(0, water - leakWater)  -- 水量不能为负
lastTime = now  -- 更新时间戳

-- 判断是否允许新请求
if water + 1 <= capacity then
    water = water + 1
    redis.call("HMSET", key, "water", water, "lastTime", lastTime)
    redis.call("EXPIRE", key, 86400)  -- 设置过期时间,避免内存溢出
    return 1  -- 允许
else
    return 0  -- 拒绝
end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容