您好,登錄后才能下訂單哦!
這篇文章主要介紹如何實現(xiàn)Golang限流器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!
限流器是服務(wù)中非常重要的一個組件,在網(wǎng)關(guān)設(shè)計、微服務(wù)、以及普通的后臺應(yīng)用中都比較常見。它可以限制訪問服務(wù)的頻次和速率,防止服務(wù)過載,被刷爆。
限流器的算法比較多,常見的比如令牌桶算法、漏斗算法、信號量等。本文主要介紹基于漏斗算法的一個限流器的實現(xiàn)。文本也提供了其他幾種開源的實現(xiàn)方法。
基于令牌桶的限流器實現(xiàn)
在golang 的官方擴(kuò)展包 time 中(github/go/time),提供了一個基于令牌桶算法的限流器的實現(xiàn)。
原理
令牌桶限流器,有兩個概念:
因此,一個令牌桶的限流器,可以限制一個時間間隔內(nèi),最多可以承載桶容量的訪問頻次。下面我們看看官方的實現(xiàn)。
實現(xiàn)
限流器的定義
下面是對一個限流器的定義:
type Limiter struct { limit Limit // 放入桶的頻率 (Limit 為 float64類型) burst int // 桶的大小 mu sync.Mutex tokens float64 // 當(dāng)前桶內(nèi)剩余令牌個數(shù) last time.Time // 最近取走token的時間 lastEvent time.Time // 最近限流事件的時間 }
其中,核心參數(shù)是 limit,burst。 burst 代表了桶的大小,從實際意義上來講,可以理解為服務(wù)可以承載的并發(fā)量大??;limit 代表了 放入桶的頻率,可以理解為正常情況下,1s內(nèi)我們的服務(wù)可以處理的請求個數(shù)。
在令牌發(fā)放后,會被保留在Reservation 對象中,定義如下:
type Reservation struct { ok bool // 是否滿足條件分配到了tokens lim *Limiter // 發(fā)送令牌的限流器 tokens int // tokens 的數(shù)量 timeToAct time.Time // 滿足令牌發(fā)放的時間 limit Limit // 令牌發(fā)放速度 }
Reservation 對象,描述了一個在達(dá)到 timeToAct 時間后,可以獲取到的令牌的數(shù)量tokens。 (因為有些需求會做預(yù)留的功能,所以timeToAct 并不一定就是當(dāng)前的時間。
限流器如何限流
官方提供的限流器有阻塞等待式的,也有直接判斷方式的,還有提供了自己維護(hù)預(yù)留式的,但核心的實現(xiàn)都是下面的reserveN 方法。
// 在 now 時間需要拿到n個令牌,最多可以等待的時間為maxFutureResrve // 結(jié)果將返回一個預(yù)留令牌的對象 func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation { lim.mu.Lock() // 首先判斷是否放入頻次是否為無窮大,如果為無窮大,說明暫時不限流 if lim.limit == Inf { // ... } // 拿到截至now 時間時,可以獲取的令牌tokens數(shù)量,上一次拿走令牌的時間last now, last, tokens := lim.advance(now) // 然后更新 tokens 的數(shù)量,把需要拿走的去掉 tokens -= float64(n) // 如果tokens 為負(fù)數(shù),說明需要等待,計算等待的時間 var waitDuration time.Duration if tokens < 0 { waitDuration = lim.limit.durationFromTokens(-tokens) } // 計算是否滿足分配條件 // ① 需要分配的大小不超過桶容量 // ② 等待時間不超過設(shè)定的等待時常 ok := n <= lim.burst && waitDuration <= maxFutureReserve // 最后構(gòu)造一個Reservation對象 r := Reservation{ ok: ok, lim: lim, limit: lim.limit, } if ok { r.tokens = n r.timeToAct = now.Add(waitDuration) } // 并更新當(dāng)前l(fā)imiter 的值 if ok { lim.last = now lim.tokens = tokens lim.lastEvent = r.timeToAct } else { lim.last = last } lim.mu.Unlock() return r }
從實現(xiàn)上看,limiter 并不是每隔一段時間更新當(dāng)前桶中令牌的數(shù)量,而是記錄了上次訪問時間和當(dāng)前桶中令牌的數(shù)量。當(dāng)再次訪問時,通過上次訪問時間計算出當(dāng)前桶中的令牌的數(shù)量,決定是否可以發(fā)放令牌。
使用
下面我們通過一個簡單的例子,學(xué)習(xí)上面介紹的限流器的使用。
limiter := rate.NewLimiter(rate.Every(100*time.Millisecond), 10) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { if limiter.Allow() {// do something log.Println("say hello") } }) _ = http.ListenAndServe(":13100", nil)
上面,每100 ms 放入令牌桶中1個令牌,所以當(dāng)批量訪問該接口時,可以看到如下結(jié)果:
2020/06/26 14:34:16 say hello 有18 條記錄
2020/06/26 14:34:17 say hello 有10 條記錄
2020/06/26 14:34:18 say hello 有10 條記錄
...
一開始漏斗滿著,可以緩解部分突發(fā)的流量。當(dāng)漏斗未空時,訪問的頻次和令牌放入的頻次變?yōu)橐恢隆?/p>
其他限流器的實現(xiàn)
uber 開源庫中基于漏斗算法實現(xiàn)了一個限流器。漏斗算法可以限制流量的請求速度,并起到削峰填谷的作用。 https://github.com/uber-go/ratelimit
滴滴開源實現(xiàn)了一個對http請求的限流器中間件??梢曰谝韵履J较蘖?。
以上是如何實現(xiàn)Golang限流器的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。