go每日一庫 [go-rate] 速率限制器

go-rate是速率限制器庫,基于 Token Bucket(令牌桶)算法實(shí)現(xiàn)箕般。 go-rate被用在LangTrend的生產(chǎn)中 用于遵守GitHub API速率限制孔祸。

速率限制可以完成一些特殊的功能需求榛做,包括但不限于服務(wù)器端垃圾郵件保護(hù)婿脸、防止api調(diào)用飽和等。

庫使用說明

構(gòu)造限流器

我們首先構(gòu)造一個(gè)限流器對(duì)象:

limiter := NewLimiter(10, 1);

這里有兩個(gè)參數(shù):

  1. 第一個(gè)參數(shù)是 r Limit利赋。代表每秒可以向 Token 桶中產(chǎn)生多少 token郁妈。Limit 實(shí)際上是 float64 的別名。
  2. 第二個(gè)參數(shù)是 b int趾断。b 代表 Token 桶的容量大小拒名。

上述的限流器的含義是:擁有一個(gè)容量為1的令牌桶,以每鈔10個(gè)的速度向桶中放令牌芋酌。

除了直接指定每秒產(chǎn)生的 Token 個(gè)數(shù)外增显,還可以用 Every 方法來指定向 Token 桶中放置 Token 的間隔,例如:

limiter := NewLimiter(Every(100 * time.Millisecond), 1);

以上就表示每 100ms 往桶中放一個(gè) Token脐帝。本質(zhì)上也就是一秒鐘產(chǎn)生 10 個(gè)同云。

消費(fèi)令牌Token

Limiter 提供了三類方法供用戶消費(fèi) Token,用戶可以每次消費(fèi)一個(gè) Token堵腹,也可以一次性消費(fèi)多個(gè) Token炸站。
而每種方法代表了當(dāng) Token 不足時(shí),各自不同的對(duì)應(yīng)手段疚顷。

Wait/WaitN

func (lim *Limiter) Wait(ctx context.Context) (err error)
func (lim *Limiter) WaitN(ctx context.Context, n int) (err error)

Wait 實(shí)際上就是 WaitN(ctx,1)旱易。

當(dāng)使用 Wait 方法消費(fèi) Token 時(shí)禁偎,如果此時(shí)桶內(nèi) Token 數(shù)組不足 (小于 N),那么 Wait 方法將會(huì)阻塞一段時(shí)間咒唆,直至 Token 滿足條件届垫。如果充足則直接返回。

這里可以看到全释,Wait 方法有一個(gè) context 參數(shù)装处。我們可以設(shè)置 context 的 Deadline 或者 Timeout,來決定此次 Wait 的最長時(shí)間浸船。

Allow/AllowN

Allow 實(shí)際上就是 AllowN(time.Now(),1)妄迁。

AllowN 方法表示,截止到某一時(shí)刻李命,目前桶中數(shù)目是否至少為 n 個(gè)登淘,滿足則返回 true,同時(shí)從桶中消費(fèi) n 個(gè) token封字。
反之返回不消費(fèi) Token黔州,false。

通常對(duì)應(yīng)這樣的線上場(chǎng)景阔籽,如果請(qǐng)求速率過快流妻,就直接丟到某些請(qǐng)求。

Reserve/ReserveN

Reserve 相當(dāng)于 ReserveN(time.Now(), 1)笆制。

ReserveN 的用法就相對(duì)來說復(fù)雜一些绅这,當(dāng)調(diào)用完成后,無論 Token 是否充足在辆,都會(huì)返回一個(gè) Reservation * 對(duì)象证薇。

你可以調(diào)用該對(duì)象的 Delay() 方法,該方法返回了需要等待的時(shí)間匆篓。如果等待時(shí)間為 0浑度,則說明不用等待。必須等到等待時(shí)間之后奕删,才能進(jìn)行接下來的工作俺泣。

或者,如果不想等待完残,可以調(diào)用 Cancel() 方法伏钠,該方法會(huì)將 Token 歸還。

使用一個(gè)偽代碼來舉例谨设,我們可以如何使用 Reserve 方法熟掂。

r := lim.Reserve()
//是否愿意等待
f !r.OK() {
    //不愿意等待直接退出
    return
}

//如果愿意等待,將等待時(shí)間拋給用戶 time.Sleep代表用戶需要等待的時(shí)間扎拣。
time.Sleep(r.Delay())
Act() // 一段時(shí)間后生成生成新的令牌赴肚,開始執(zhí)行相關(guān)邏輯

動(dòng)態(tài)調(diào)整速率

Limiter 支持可以調(diào)整速率和桶大兴囟濉:

  1. SetLimit(Limit) 改變放入 Token 的速率
  2. SetBurst(int) 改變 Token 桶大小

有了這兩個(gè)方法,可以根據(jù)現(xiàn)有環(huán)境和條件以及我們的需求誉券,動(dòng)態(tài)地改變 Token 桶大小和速率指厌。

案例1-單位時(shí)間只允許一次郵件發(fā)送操作

客戶端軟件客戶點(diǎn)擊發(fā)送郵件,如果客戶一秒鐘內(nèi)點(diǎn)擊10次踊跟,就會(huì)發(fā)送10次踩验,這明顯是不合適的。如果使用速率限制商玫,我們就可以限制一秒內(nèi)只能發(fā)送一次箕憾,實(shí)現(xiàn)方法為:

(令牌桶)容量為1,速度為每一秒生成一個(gè)令牌拳昌,這樣可以保證一秒鐘只會(huì)被執(zhí)行一次袭异,偽代碼實(shí)現(xiàn)如下

//初始化 limiter 每秒生成1個(gè)令牌,令牌桶容量為20
limiter := rate.NewLimiter(rate.Every(time.Second), 1)
//模擬單位時(shí)間執(zhí)行多次操作
for i := 0; i < 5; i++ {
    if limiter.Allow() {
        fmt.Println("發(fā)送郵件")
    } else {
        fmt.Println("請(qǐng)求多次炬藤,過濾")
    }
}
if limiter.Allow() {
        fmt.Println("發(fā)送郵件")
}

執(zhí)行結(jié)果

發(fā)送郵件
請(qǐng)求多次御铃,過濾
請(qǐng)求多次,過濾
請(qǐng)求多次沈矿,過濾
請(qǐng)求多次畅买,過濾
發(fā)送郵件

我們發(fā)現(xiàn),第一次執(zhí)行是可以被允許的因?yàn)榈谝淮蔚牧钆票辉试S细睡,之后的請(qǐng)求失敗是因?yàn)檫€沒有生成新的令牌,所以需要等待1秒帝火,之后又可以進(jìn)行發(fā)送郵件操作溜徙。

通過這樣一個(gè)案例,相信大家對(duì)令牌桶的實(shí)現(xiàn)場(chǎng)景有了一個(gè)基本的了解犀填。

案例2——令牌取出單個(gè)和多個(gè)

初始化令牌桶容量為20蠢壹,設(shè)置每100毫秒生成一個(gè)令牌,即1秒生產(chǎn)10個(gè)令牌九巡。編碼測(cè)試功能

//初始化 limiter 每秒10個(gè)令牌图贸,令牌桶容量為20
limiter := rate.NewLimiter(rate.Every(time.Millisecond*100), 20)
for i := 0; i < 25; i++ {
    if limiter.Allow() {
        fmt.Println("success") //do something
    } else {
        fmt.Println("busy")
    }
}

//阻塞直到獲取足夠的令牌或者上下文取消
ctx, _ := context.WithTimeout(context.Background(), time.Second*2)
fmt.Println("start get token", time.Now())
err := limiter.WaitN(ctx, 20)
if err != nil {
    fmt.Println("error", err)
    return
}
fmt.Println("success get token", time.Now())

第二段編碼阻塞的場(chǎng)景在于,一次性取出20個(gè)令牌給予2秒的等待時(shí)間冕广,如果有20個(gè)令牌可以取出打印成功消息疏日,如果2秒等待時(shí)間內(nèi)沒有20個(gè)令牌可以取出,程序直接退出撒汉,即失敗沟优。

參考

go-rate的使用

go-rate

Golang 標(biāo)準(zhǔn)庫限流器 time/rate 使用介紹

Golang限流器rate使用

END

歡迎關(guān)注公眾號(hào) 程序員工具集 ???? 致力于分享優(yōu)秀的開源項(xiàng)目、學(xué)習(xí)資源 睬辐、常用工具

回復(fù)關(guān)鍵詞“關(guān)注禮包”挠阁,送你一份最全的程序員技能圖譜宾肺。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市侵俗,隨后出現(xiàn)的幾起案子锨用,更是在濱河造成了極大的恐慌,老刑警劉巖隘谣,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件增拥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡洪橘,警方通過查閱死者的電腦和手機(jī)跪者,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來熄求,“玉大人渣玲,你說我怎么就攤上這事〉芡恚” “怎么了忘衍?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長卿城。 經(jīng)常有香客問我枚钓,道長,這世上最難降的妖魔是什么瑟押? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任搀捷,我火速辦了婚禮,結(jié)果婚禮上多望,老公的妹妹穿的比我還像新娘嫩舟。我一直安慰自己,他們只是感情好怀偷,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布家厌。 她就那樣靜靜地躺著,像睡著了一般椎工。 火紅的嫁衣襯著肌膚如雪饭于。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天维蒙,我揣著相機(jī)與錄音掰吕,去河邊找鬼。 笑死颅痊,一個(gè)胖子當(dāng)著我的面吹牛畴栖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播八千,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼吗讶,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼燎猛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起照皆,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤重绷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后膜毁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體昭卓,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年瘟滨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了候醒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杂瘸,死狀恐怖倒淫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情败玉,我是刑警寧澤敌土,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站运翼,受9級(jí)特大地震影響返干,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜血淌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一矩欠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧悠夯,春花似錦晚顷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瞳氓。三九已至策彤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間匣摘,已是汗流浹背店诗。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留音榜,地道東北人庞瘸。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像赠叼,于是被迫代替她去往敵國和親擦囊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子违霞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344