亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

使用Redis實現(xiàn)令牌桶算法原理解析

 更新時間:2021年12月08日 08:51:33   作者:波斯馬  
這篇文章主要介紹了使用Redis實現(xiàn)令牌桶算法,該算法可以應(yīng)對短暫的突發(fā)流量,這對于現(xiàn)實環(huán)境中流量不怎么均勻的情況特別有用,不會頻繁的觸發(fā)限流,對調(diào)用方比較友好,需要的朋友可以參考下

在限流算法中有一種令牌桶算法,該算法可以應(yīng)對短暫的突發(fā)流量,這對于現(xiàn)實環(huán)境中流量不怎么均勻的情況特別有用,不會頻繁的觸發(fā)限流,對調(diào)用方比較友好。

例如,當(dāng)前限制10qps,大多數(shù)情況下不會超過此數(shù)量,但偶爾會達到30qps,然后很快就會恢復(fù)正常,假設(shè)這種突發(fā)流量不會對系統(tǒng)穩(wěn)定性產(chǎn)生影響,我們可以在一定程度上允許這種瞬時突發(fā)流量,從而為用戶帶來更好的可用性體驗。這就是使用令牌桶算法的地方。

令牌桶算法原理

如下圖所示,該算法的基本原理是:有一個容量為X的令牌桶,每Y單位時間內(nèi)將Z個令牌放入該桶。如果桶中的令牌數(shù)量超過X,那么它將被丟棄。處理請求時,需要先從令牌桶中取出令牌,如果拿到了令牌,則繼續(xù)處理;如果拿不到令牌,則拒絕請求。

可以看出,在令牌桶算法中設(shè)置X,Y和Z的數(shù)量尤為重要。Z應(yīng)該比每Y單位時間內(nèi)的請求數(shù)稍大,系統(tǒng)將長時間處于此狀態(tài);X是系統(tǒng)允許的瞬時最大請求數(shù),并且系統(tǒng)不應(yīng)該長時間處于此狀態(tài),否則就會頻繁觸發(fā)限流,此時表明流量出現(xiàn)了超預(yù)期的情況,需要及時調(diào)查原因并采取相應(yīng)措施。

Redis實現(xiàn)令牌桶算法

之前看過有些程序?qū)崿F(xiàn)的令牌桶,其向桶中放入令牌的方法是啟動一個線程,每隔Y單位時間增加一次令牌數(shù)量,或者在Timer中定時執(zhí)行這一過程。我不太滿意這種方法, 原因有二,一是浪費線程資源,二是因為調(diào)度的問題執(zhí)行時間不精確。

這里確定令牌桶中令牌數(shù)量的方法是通過計算得出,首先算出從上次請求到這次請求經(jīng)過了多長時間,是否達到發(fā)令牌的時間閾值,然后增加的令牌數(shù)是多少,這些令牌能夠放到桶中的是多少。

Talk is cheap!

下邊就來看看Redis中怎么實現(xiàn)的,因為涉及到多次與Redis的交互,這里為了提高限流處理的吞吐量,減少程序與Redis的交互次數(shù),采用了Redis支持的Lua script,Lua script的執(zhí)行是原子的,所以也不用擔(dān)心出現(xiàn)臟數(shù)據(jù)的問題。

代碼節(jié)選自 FireflySoft.RateLimit ,它不僅支持普通主從部署Redis,還支持集群Redis,所以吞吐量可以通過水平擴展的方式進行提升。為了方便閱讀,這里增加一些注釋,實際是沒有的。

-- 定義返回值,是個數(shù)組,包含:是否觸發(fā)限流(1限流 0通過)、當(dāng)前桶中的令牌數(shù)
local ret={}
ret[1]=0
-- Redis集群分片Key,KEYS[1]是限流目標
local cl_key = '{' .. KEYS[1] .. '}'

-- 獲取限流懲罰的當(dāng)前設(shè)置,觸發(fā)限流懲罰時會寫一個有過期時間的KV
-- 如果存在限流懲罰,則返回結(jié)果[1,-1]
local lock_key=cl_key .. '-lock'
local lock_val=redis.call('get',lock_key)
if lock_val == '1' then
    ret[1]=1
    ret[2]=-1
    return ret;
end

-- 這里省略部分代碼

-- 獲取[上次向桶中投放令牌的時間],如果沒有設(shè)置過這個投放時間,則令牌桶也不存在,此時:
-- 一種情況是:首次執(zhí)行,此時定義令牌桶就是滿的。
-- 另一種情況是:較長時間沒有執(zhí)行過限流處理,導(dǎo)致承載這個時間的KV被釋放了,
-- 這個過期時間會超過自然投放令牌到桶中直到桶滿的時間,所以令牌桶也應(yīng)該是滿的。
local last_time=redis.call('get',st_key)
if(last_time==false)
then
 -- 本次執(zhí)行后剩余令牌數(shù)量:桶的容量- 本次執(zhí)行消耗的令牌數(shù)量
    bucket_amount = capacity - amount;
    -- 將這個令牌數(shù)量更新到令牌桶中,同時這里有個過期時間,如果長時間不執(zhí)行這個程序,令牌桶KV會被回收
    redis.call('set',KEYS[1],bucket_amount,'PX',key_expire_time)
    -- 設(shè)置[上次向桶中放入令牌的時間],后邊計算應(yīng)放入桶中的令牌數(shù)量時會用到
    redis.call('set',st_key,start_time,'PX',key_expire_time)
    -- 返回值[當(dāng)前桶中的令牌數(shù)]
    ret[2]=bucket_amount
    -- 無需其它處理
    return ret
end

-- 令牌桶存在,獲取令牌桶中的當(dāng)前令牌數(shù)
local current_value = redis.call('get',KEYS[1])
current_value = tonumber(current_value)

-- 判斷是不是該放入新令牌到桶中了:當(dāng)前時間-上次投放的時間 >= 投放的時間間隔
last_time=tonumber(last_time)
local last_time_changed=0
local past_time=current_time-last_time
if(past_time<inflow_unit)
then
 -- 不到投放的時候,直接從令牌桶中取走令牌
    bucket_amount=current_value-amount
else
 -- 需要放入一些令牌, 預(yù)計投放數(shù)量 = (距上次投放過去的時間/投放的時間間隔)*每單位時間投放的數(shù)量
    local past_inflow_unit_quantity = past_time/inflow_unit
    past_inflow_unit_quantity=math.floor(past_inflow_unit_quantity)
    last_time=last_time+past_inflow_unit_quantity*inflow_unit
    last_time_changed=1
    local past_inflow_quantity=past_inflow_unit_quantity*inflow_quantity_per_unit
    bucket_amount=current_value+past_inflow_quantity-amount
end

-- 這里省略部分代碼

ret[2]=bucket_amount

-- 如果桶中剩余數(shù)量小于0,則看看是否需要限流懲罰,如果需要則寫入一個懲罰KV,過期時間為懲罰的秒數(shù)
if(bucket_amount<0)
then
    if lock_seconds>0 then
        redis.call('set',lock_key,'1','EX',lock_seconds,'NX')
    end
    ret[1]=1
    return ret
end

-- 來到這里,代表可以成功扣減令牌,則需要更新令牌桶KV
if last_time_changed==1 then
    redis.call('set',KEYS[1],bucket_amount,'PX',key_expire_time)
 -- 有新投放,更新[上次投放時間]為本次投放時間
    redis.call('set',st_key,last_time,'PX',key_expire_time)
else
    redis.call('set',KEYS[1],bucket_amount,'PX',key_expire_time)
end
return ret

通過以上代碼,可以看出,其主要處理過程是:

1、判斷有沒有被限流懲罰,有則直接返回,無則進入下一步。

2、判斷令牌桶是否存在,不存在則先創(chuàng)建令牌桶,然后扣減令牌返回,存在則進入下一步。

3、判斷是否需要投放令牌,不需要則直接扣減令牌,需要則先投放令牌再扣減令牌。

4、判斷扣減后的令牌數(shù),如果小于0則返回限流,同時設(shè)置限流懲罰,如果大于等于0則進入下一步。

5、更新桶中的令牌數(shù)到Redis。

你可以在任何一種開發(fā)語言的Redis庫中提交并運行這段Lua script腳本,如果你使用的是.NET平臺,可以參考這篇文章:ASP.NET Core中使用令牌桶限流

關(guān)于FireflySoft.RateLimit

FireflySoft.RateLimit 是一個基于 .NET Standard 的限流類庫,其內(nèi)核簡單輕巧,能夠靈活應(yīng)對各種需求的限流場景。

其主要特點包括:

  • 多種限流算法:內(nèi)置固定窗口、滑動窗口、漏桶、令牌桶四種算法,還可自定義擴展。
  • 多種計數(shù)存儲:目前支持內(nèi)存、Redis兩種存儲方式。
  • 分布式友好:通過Redis存儲支持分布式程序統(tǒng)一計數(shù)。
  • 限流目標靈活:可以從請求中提取各種數(shù)據(jù)用于設(shè)置限流目標。
  • 支持限流懲罰:可以在客戶端觸發(fā)限流后鎖定一段時間不允許其訪問。
  • 動態(tài)更改規(guī)則:支持程序運行時動態(tài)更改限流規(guī)則。
  • 自定義錯誤:可以自定義觸發(fā)限流后的錯誤碼和錯誤消息。
  • 普適性:原則上可以滿足任何需要限流的場景。

Github開源地址:https://github.com/bosima/FireflySoft.RateLimit/blob/master/README.zh-CN.md

到此這篇關(guān)于使用Redis實現(xiàn)令牌桶算法的文章就介紹到這了,更多相關(guān)Redis令牌桶算法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis底層數(shù)據(jù)結(jié)構(gòu)之dict、ziplist、quicklist詳解

    Redis底層數(shù)據(jù)結(jié)構(gòu)之dict、ziplist、quicklist詳解

    本文給大家詳細介紹了Redis的底層數(shù)據(jù)結(jié)構(gòu):dict、ziplist、quicklist的相關(guān)知識,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-09-09
  • Redis數(shù)據(jù)庫的安裝和配置教程詳解

    Redis數(shù)據(jù)庫的安裝和配置教程詳解

    這篇文章主要介紹了Redis數(shù)據(jù)庫的安裝和配置?,主要包括Linux環(huán)境安裝Redis和windows環(huán)境安裝Redis,redis客戶端安裝方法,本文通過圖文并茂的形式給大家介紹的非常詳細,需要的朋友可以參考下
    2022-05-05
  • Redis為什么默認有16個數(shù)據(jù)庫問題

    Redis為什么默認有16個數(shù)據(jù)庫問題

    這篇文章主要介紹了Redis為什么默認有16個數(shù)據(jù)庫問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • redis禁止幾個危險命令的方法

    redis禁止幾個危險命令的方法

    今天小編就為大家分享一篇redis禁止幾個危險命令的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • Redis中鍵值過期操作示例詳解

    Redis中鍵值過期操作示例詳解

    這篇文章主要給大家介紹了關(guān)于Redis中鍵值過期操作的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用Redis具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • Redis高并發(fā)問題的解決方法

    Redis高并發(fā)問題的解決方法

    這篇文章主要介紹了Redis高并發(fā)問題的解決辦法,具有很好的參考價值,感興趣的小伙伴們可以參考一下,具體如下:
    2018-05-05
  • Redis進行相關(guān)優(yōu)化詳解

    Redis進行相關(guān)優(yōu)化詳解

    這篇文章主要介紹了Redis進行相關(guān)優(yōu)化,Redis在項目中進行廣泛使用,那么在日常的開發(fā)過程中,我們在使用Redis的過程中需要注意那些呢?本文將從三個維度來講解如何進行Redis的優(yōu)化
    2022-08-08
  • redis刪除key下所有value步驟詳解

    redis刪除key下所有value步驟詳解

    在使用Redis時,經(jīng)常需要刪除某個key下的所有value,本文就來詳細的介紹一下redis刪除key下所有value步驟,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • redis集群搭建過程(非常詳細,適合新手)

    redis集群搭建過程(非常詳細,適合新手)

    這篇文章主要介紹了redis集群搭建過程,Redis集群至少需要3個節(jié)點,因為投票容錯機制要求超過半數(shù)節(jié)點認為某個節(jié)點掛了該節(jié)點才是掛了,所以2個節(jié)點無法構(gòu)成集群,具體搭建過程跟隨小編一起看看吧
    2021-11-11
  • redis計數(shù)器與數(shù)量控制的實現(xiàn)

    redis計數(shù)器與數(shù)量控制的實現(xiàn)

    使用Redis計數(shù)器可以輕松地解決數(shù)量控制的問題,同時還能有效地提高應(yīng)用的性能,本文主要介紹了redis計數(shù)器與數(shù)量控制的實現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2023-12-12

最新評論