本主題為系列文章,分上下兩篇。本文主要介紹
time/rate
的具體使用方法,另外一篇文章《Golang限流器time/rate實現剖析》則着重介紹其內部實現原理。
限流器是后台服務中的非常重要的組件,可以用來限制請求速率,保護服務,以免服務過載。
限流器的實現方法有很多種,例如滑動窗口法、Token Bucket、Leaky Bucket等。
其實golang標准庫中就自帶了限流算法的實現,即golang.org/x/time/rate
。
該限流器是基於Token Bucket(令牌桶)實現的。
簡單來說,令牌桶就是想象有一個固定大小的桶,系統會以恆定速率向桶中放Token,桶滿則暫時不放。
而用戶則從桶中取Token,如果有剩余Token就可以一直取。如果沒有剩余Token,則需要等到系統中被放置了Token才行。
本文則主要集中介紹下該組件的具體使用方法:
構造一個限流器
我們可以使用以下方法構造一個限流器對象:
limiter := NewLimiter(10, 1); |
這里有兩個參數:
- 第一個參數是
r Limit
。代表每秒可以向Token桶中產生多少token。Limit實際上是float64的別名。 - 第二個參數是
b int
。b代表Token桶的容量大小。
那么,對於以上例子來說,其構造出的限流器含義為,其令牌桶大小為1, 以每秒10個Token的速率向桶中放置Token。
除了直接指定每秒產生的Token個數外,還可以用Every方法來指定向Token桶中放置Token的間隔,例如:
limit := Every(100 * time.Millisecond); |
以上就表示每100ms往桶中放一個Token。本質上也就是一秒鍾產生10個。
Limiter提供了三類方法供用戶消費Token,用戶可以每次消費一個Token,也可以一次性消費多個Token。
而每種方法代表了當Token不足時,各自不同的對應手段。
Wait/WaitN
func (lim *Limiter) Wait(ctx context.Context) (err error) |
Wait實際上就是WaitN(ctx,1)
。
當使用Wait方法消費Token時,如果此時桶內Token數組不足(小於N),那么Wait方法將會阻塞一段時間,直至Token滿足條件。如果充足則直接返回。
這里可以看到,Wait方法有一個context參數。
我們可以設置context的Deadline或者Timeout,來決定此次Wait的最長時間。
Allow/AllowN
func (lim *Limiter) Allow() bool |
Allow實際上就是AllowN(time.Now(),1)
。
AllowN方法表示,截止到某一時刻,目前桶中數目是否至少為n個,滿足則返回true,同時從桶中消費n個token。
反之返回不消費Token,false。
通常對應這樣的線上場景,如果請求速率過快,就直接丟到某些請求。
Reserve/ReserveN
func (lim *Limiter) Reserve() *Reservation |
Reserve相當於ReserveN(time.Now(), 1)
。
ReserveN的用法就相對來說復雜一些,當調用完成后,無論Token是否充足,都會返回一個Reservation*對象。
你可以調用該對象的Delay()方法,該方法返回了需要等待的時間。如果等待時間為0,則說明不用等待。
必須等到等待時間之后,才能進行接下來的工作。
或者,如果不想等待,可以調用Cancel()方法,該方法會將Token歸還。
舉一個簡單的例子,我們可以這么使用Reserve方法。
r := lim.Reserve() |
動態調整速率
Limiter支持可以調整速率和桶大小:
- SetLimit(Limit) 改變放入Token的速率
- SetBurst(int) 改變Token桶大小
有了這兩個方法,可以根據現有環境和條件,根據我們的需求,動態的改變Token桶大小和速率