go-rate
https://pkg.go.dev/github.com/beefsack/go-rate#section-sourcefiles
用法
new():創(chuàng)建一個(gè)實(shí)例wait():用來(lái)阻塞等待try():返回兩個(gè)值,一個(gè)是表示是否可以立刻執(zhí)行,另一個(gè)是需要延遲多久執(zhí)行
實(shí)現(xiàn)
它的實(shí)現(xiàn)是,任何一段時(shí)間interval內(nèi),不能有超過limit個(gè)請(qǐng)求
維護(hù)一個(gè)隊(duì)列,隊(duì)列頭是最早的時(shí)間,隊(duì)列元素不會(huì)刪(相當(dāng)于固定的token數(shù)),到了limit后就從頭部移動(dòng)到尾部
假如當(dāng)前隊(duì)列中數(shù)目不超過limit個(gè)(最一開始的情況)
后面的情況就是:每次對(duì)比下隊(duì)列中最早的時(shí)間和當(dāng)前時(shí)間,最早時(shí)間超過了當(dāng)前時(shí)間說(shuō)明已經(jīng)被處理過了相當(dāng)于有空閑token了,所以可以處理(設(shè)置時(shí)間,移動(dòng)到隊(duì)列尾),不用sleep;否則就sleep這個(gè)差值。
// Try returns true if under the rate limit, or false if over and the
// remaining time before the rate limit expires.
func (r *RateLimiter) Try() (ok bool, remaining time.Duration) {
r.mtx.Lock()
defer r.mtx.Unlock()
now := time.Now()
if l := r.times.Len(); l < r.limit {
r.times.PushBack(now)
return true, 0
}
frnt := r.times.Front()
if diff := now.Sub(frnt.Value.(time.Time)); diff < r.interval {
return false, r.interval - diff
}
frnt.Value = now
r.times.MoveToBack(frnt)
return true, 0
}
關(guān)鍵的操作是:if diff := now.Sub(frnt.Value.(time.Time)); diff < r.interval
走到這里隊(duì)列是已經(jīng)滿了,必須擠掉一個(gè)人,且是擠掉最老的人,frnt是隊(duì)列中最老的時(shí)間,所以當(dāng)前時(shí)間和他相比,只要當(dāng)前時(shí)間比最老的時(shí)間老且超過的時(shí)間小于r.interval - diff。
設(shè)計(jì)的含義是:r.interval這段時(shí)間內(nèi)只能有r.limit個(gè)元素,隊(duì)列長(zhǎng)度最大就是r.limit;隊(duì)列從頭到尾時(shí)間是越來(lái)越新(大)。所以如果來(lái)一個(gè)新的請(qǐng)求,隊(duì)列又滿了,那么需要這個(gè)新請(qǐng)求的時(shí)間比最老的時(shí)間大且要大r.interval這么久,因?yàn)槌^r.interval,這個(gè)最老的請(qǐng)求就可以移除了
aa
其他
它的處理不是時(shí)間片均勻的, 比如說(shuō)設(shè)置1s有100個(gè)token,有100個(gè)線程來(lái)?yè)專琧pu處理很快的話,可能0.1秒就處理完了剩下0.9秒都是閑的,這樣的話CPU峰值會(huì)高但其他時(shí)間可能很閑。即CPU的工作不是很均勻。
增加分時(shí)間段不同的限流的功能
隊(duì)列長(zhǎng)度是動(dòng)態(tài)變化的,不同的時(shí)間段,隊(duì)列長(zhǎng)度不同