Golang官方限流器庫(kù)實(shí)現(xiàn)限流示例詳解
前言
在翻Golang官方庫(kù)的過(guò)程中,發(fā)現(xiàn)一個(gè)有趣的庫(kù)golang.org/x/time ,里面只有一個(gè)類rate,研究了一下發(fā)現(xiàn)它是一個(gè)限流器,實(shí)現(xiàn)了很多的功能,當(dāng)然它的核心原理并不復(fù)雜,也就是令牌桶算法。
令牌桶算法的原理是:令牌桶會(huì)不斷地把令牌添加到桶里,而請(qǐng)求會(huì)從桶中獲取令牌,只有擁有令牌地請(qǐng)求才能被接受。因?yàn)橥爸锌梢蕴崆氨A粢恍┝钆?,所以它允許一定地突發(fā)流量通過(guò)。
例子
下面是限流算法常見的寫法,首先判斷是否有令牌,如果有就通過(guò),否則直接失敗。
package main
import (
"fmt"
"time"
"golang.org/x/time/rate"
)
func main() {
// 每0.1秒生成一個(gè)令牌,也就是一秒10個(gè)令牌,最大保留令牌上限10
l := rate.NewLimiter(rate.Every(time.Second/10), 10)
for i := 0; i < 10; i++ {
go func(i int) {
for {
// 判斷是否有令牌,如果有就輸出
if l.Allow() {
fmt.Printf("allow %d\n", i)
}
// 每0.5秒請(qǐng)求一次
time.Sleep(time.Second / 2)
}
}(i)
}
time.Sleep(time.Second * 10)
}
上面的rate.Every(time.Second/10)會(huì)返回一個(gè)Limit類型,代表每秒生成多少個(gè)令牌。
這個(gè)庫(kù)還提供了另外一種寫法,等待直到有令牌為止(或超時(shí)):
func main() {
l := rate.NewLimiter(rate.Every(time.Second/10), 100)
for i := 0; i < 10; i++ {
go func(i int) {
for {
// 等待直到有令牌
if err := l.Wait(context.TODO()); err != nil {
} else {
fmt.Printf("allow %d\n", i)
}
time.Sleep(time.Second / 2)
}
}(i)
}
time.Sleep(time.Second * 10)
}
這樣在某些場(chǎng)景下我們可以讓請(qǐng)求等待一會(huì),而不是直接失敗。
還有一個(gè)更加特殊的請(qǐng)求令牌方式,也就是先預(yù)留令牌,到指定時(shí)間不再需要去獲取令牌,直接執(zhí)行操作即可:
func main() {
l := rate.NewLimiter(rate.Every(time.Second/10), 10)
for i := 0; i < 10; i++ {
go func(i int) {
for {
// 先預(yù)留令牌
if r := l.Reserve(); r.OK() {
// 休眠直到令牌生效
time.Sleep(r.Delay())
fmt.Printf("allow %d\n", i)
}
time.Sleep(time.Second / 2)
}
}(i)
}
time.Sleep(time.Second * 10)
}
當(dāng)然,如果預(yù)留的令牌不想使用了,也可以使用r.Cancel()歸還已預(yù)留的令牌。
上面的Allow()、Wait()、Reserve()都是一次消耗一個(gè)令牌,其實(shí)都有對(duì)應(yīng)的AllowN()、WaitN()、ReserveN()方法,一次消耗N個(gè)令牌,這樣就可以根據(jù)任務(wù)消耗的資源靈活的消耗令牌。
實(shí)現(xiàn)
不管我們從Allow()、Wait()還是Reserve()進(jìn)去,最終都會(huì)進(jìn)入到reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation 方法:
首先在進(jìn)入方法的時(shí)候,會(huì)先處理兩種特殊情況:
// 如果無(wú)限生成令牌,則直接返回
if lim.limit == Inf {
return Reservation{
ok: true,
lim: lim,
tokens: n,
timeToAct: now,
}
// 如果不會(huì)生成令牌,則在初始令牌里面拿,直到拿完為止
} else if lim.limit == 0 {
var ok bool
if lim.burst >= n {
ok = true
lim.burst -= n
}
return Reservation{
ok: ok,
lim: lim,
tokens: lim.burst,
timeToAct: now,
}
}
然后重新計(jì)算當(dāng)前有多少令牌,減去要消耗的令牌:
// 計(jì)算當(dāng)前有多少令牌
now, last, tokens := lim.advance(now)
// 減去要消耗的N個(gè)令牌
tokens -= float64(n)
// 如果剩余令牌為負(fù)數(shù),那么計(jì)算一下要等待多久才能拿到令牌
var waitDuration time.Duration
if tokens < 0 {
waitDuration = lim.limit.durationFromTokens(-tokens)
}
// 判斷請(qǐng)求是否成功,maxFutureReserve代表最大可以等待的時(shí)間,也就是請(qǐng)求能否接收拿到令牌需要等待的時(shí)間
ok := n <= lim.burst && waitDuration <= maxFutureReserve
余下的代碼就是更新限流器的狀態(tài)。
小結(jié)
可以看到這個(gè)令牌桶限流器實(shí)現(xiàn)的功能非常的豐富,如果需要令牌桶限流器,可以優(yōu)先考慮使用這個(gè)實(shí)現(xiàn)。
以上就是Golang官方限流器庫(kù)使用示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Golang官方限流器庫(kù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
重學(xué)Go語(yǔ)言之基礎(chǔ)數(shù)據(jù)類型詳解
Go語(yǔ)言有非常強(qiáng)大的數(shù)據(jù)類型系統(tǒng),其支持的數(shù)據(jù)類型大體上可分為四類:基礎(chǔ)數(shù)據(jù)類型、引用數(shù)據(jù)類型、接口類型、復(fù)合類型。本文就來(lái)講講它們各自的用法吧2023-02-02
Golang實(shí)現(xiàn)多存儲(chǔ)驅(qū)動(dòng)設(shè)計(jì)SDK案例
這篇文章主要介紹了Golang實(shí)現(xiàn)多存儲(chǔ)驅(qū)動(dòng)設(shè)計(jì)SDK案例,Gocache是一個(gè)基于Go語(yǔ)言編寫的多存儲(chǔ)驅(qū)動(dòng)的緩存擴(kuò)展組件,更多具體內(nèi)容感興趣的小伙伴可以參考一下2022-09-09
Go語(yǔ)言設(shè)置JSON的默認(rèn)值操作
這篇文章主要介紹了Go語(yǔ)言設(shè)置JSON的默認(rèn)值操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
深入解析快速排序算法的原理及其Go語(yǔ)言版實(shí)現(xiàn)
這篇文章主要介紹了快速排序算法的原理及其Go語(yǔ)言版實(shí)現(xiàn),文中對(duì)于快速算法的過(guò)程和效率有較為詳細(xì)的說(shuō)明,需要的朋友可以參考下2016-04-04
go語(yǔ)言簡(jiǎn)單的處理http請(qǐng)求的函數(shù)實(shí)例
這篇文章主要介紹了go語(yǔ)言簡(jiǎn)單的處理http請(qǐng)求的函數(shù),實(shí)例分析了Go語(yǔ)言處理http請(qǐng)求的技巧,需要的朋友可以參考下2015-03-03
GO語(yǔ)言標(biāo)準(zhǔn)錯(cuò)誤處理機(jī)制error用法實(shí)例
這篇文章主要介紹了GO語(yǔ)言標(biāo)準(zhǔn)錯(cuò)誤處理機(jī)制error用法,實(shí)例分析了錯(cuò)誤處理機(jī)制的具體用法,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12
Golang中調(diào)用deepseekr1的教程詳解
這篇文章主要為大家詳細(xì)介紹了Golang中調(diào)用deepseekr1的相關(guān)教程,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解下2025-02-02

