漏斗算法(Funnel Algorithm

漏斗算法(Funnel Algorithm)

一、核心原理(圖示+說明)

+-----------------------+
|                       |
|      待處理請求       |  ← 新請求從頂部注入
|      (水量)         |
|                       |
+-----------------------+
|       漏嘴            |  ← 按固定速率(leakRate)流出請求
+-----------------------+
    ↑
    超出容量的請求從頂部溢出(被拒絕)

(示意圖說明:漏斗上寬下窄,頂部接收請求,底部以固定速率漏水,超出容量的請求從頂部溢出)

  • 核心思想:模擬漏斗盛水→漏水的過程,控制請求流量:
    • 頂部:接收新請求(像水流注入漏斗)。
    • 容量:漏斗最多能裝的水量(最大積壓請求數(shù))。
    • 漏嘴:底部固定大小的出口,以固定速率漏水(單位時間允許通過的請求數(shù))。
    • 溢出:當注入速度>漏水速度,水滿后多余的水溢出(請求被拒絕)。

二、核心參數(shù)(4個關(guān)鍵指標)

參數(shù)名 含義 示例值
capacity 漏斗總?cè)萘浚ㄗ畲罂煞e壓請求數(shù)) 100(個)
leakRate 漏嘴速率(單位時間允許通過的請求數(shù)) 10(個/秒)
water 當前水量(當前積壓的請求數(shù)) 30(個)
lastTime 上次更新水量的時間戳(秒) 1620000000

三、執(zhí)行流程(動態(tài)調(diào)整邏輯),每一次請求都要執(zhí)行

每次請求到來時,按以下步驟處理(結(jié)合圖示理解):

  1. 計算時間差

    上次更新時間 →——————— deltaTime ———————→ 當前時間
    

    (當前時間 - 上次更新時間 = deltaTime,即兩次請求的間隔)

  2. 計算漏水量
    這段時間內(nèi)漏嘴漏掉的水量:
    漏水量 = deltaTime × leakRate
    (例:間隔3秒,漏嘴速率10個/秒 → 漏水量=3×10=30)

  3. 更新當前水量
    水量隨時間減少,且不能為負:
    water = max(0, 舊water - 漏水量)
    (例:舊water=50,漏水量=30 → 新water=20)

  4. 判斷是否允許請求

    +-----------------------+
    |  當前水量:20          |
    |                       |  → 新請求到來,判斷 20+1 ≤ 100?
    +-----------------------+     → 是 → 允許(水量變?yōu)?1)
                                  → 否 → 拒絕
    
    • water + 1 ≤ capacity:允許請求(water+1,更新lastTime)。
    • 否則:拒絕請求(漏斗已滿,溢出)。

四、優(yōu)缺點與適用場景

維度 內(nèi)容
優(yōu)點 1. 長期流量嚴格限制在漏嘴速率內(nèi);
2. 邏輯簡單,易實現(xiàn)。
缺點 1. 高并發(fā)下需頻繁計算時間差,對性能有要求;
2. 分布式場景需中間件保證一致性。
適用場景 接口限流、秒殺流量控制、用戶行為頻率限制(如評論、登錄)等。

五、分布式實現(xiàn)關(guān)鍵點(Redis+Lua)

  1. 原子性保證:用Lua腳本封裝整個流程,避免并發(fā)修改導致的水量誤差。
  2. 狀態(tài)存儲:Redis的Hash結(jié)構(gòu)存儲4個核心參數(shù)(water、lastTime等)。
  3. 性能優(yōu)化:連接池復用Redis連接、本地限流前置過濾請求、Redis集群分流。

六、具體的Lua腳本

-- 漏斗限流腳本:key為限流對象(如"user:123"),返回1表示允許,0表示拒絕
local key = KEYS[1]
local capacity = tonumber(ARGV[1])  -- 漏斗容量
local leakRate = tonumber(ARGV[2])  -- 漏嘴速率(請求/秒)

-- 從Redis獲取當前漏斗狀態(tài)(不存在則初始化)
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秒內(nèi)漏掉的水量
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)  -- 設置過期時間,避免內(nèi)存溢出
    return 1  -- 允許
else
    return 0  -- 拒絕
end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容