速率限制是控制資源利用和維持服務品質的重要機制。通過goroutines
,channel
和ticker
都可以優雅地支援速率限制。
首先我們來看一下基本速率限制。假設想限制對傳入請求的處理。我們會在相同名稱的通道上放送這些要求。
這個限制器通道將每200
毫秒接收一個值。這是速率限制方案中的調節器。
通過在服務每個請求之前阻塞來自限制器通道的接收,我們限制自己每200
毫秒接收1
個請求。
我們可能希望在速率限制方案中允許短脈衝串請求,同時保持總體速率限制。可以通過緩衝的限制器通道來實現。這個burstyLimiter
通道將允許最多3
個事件的突發。
填充通道以表示允許突發。
每200
毫秒,將嘗試向burstyLimiter
新增一個新值,最大限制為3
。現在模擬5
個更多的傳入請求。這些傳入請求中的前3
個未超過burstyLimiter
值。
執行程式後,就會看到第一批請求每?200
毫秒處理一次。
對於第二批請求,程式會立即服務前3
個,因為突發速率限制,然後剩餘2
服務都具有?200ms
延遲。
所有的範例程式碼,都放在
F:\worksp\golang
目錄下。安裝Go程式設計環境請參考:/2/23/798.html
rate-limiting.go
的完整程式碼如下所示 -
package main
import "time"
import "fmt"
func main() {
// First we'll look at basic rate limiting. Suppose
// we want to limit our handling of incoming requests.
// We'll serve these requests off a channel of the
// same name.
requests := make(chan int, 5)
for i := 1; i <= 5; i++ {
requests <- i
}
close(requests)
// This `limiter` channel will receive a value
// every 200 milliseconds. This is the regulator in
// our rate limiting scheme.
limiter := time.Tick(time.Millisecond * 200)
// By blocking on a receive from the `limiter` channel
// before serving each request, we limit ourselves to
// 1 request every 200 milliseconds.
for req := range requests {
<-limiter
fmt.Println("request", req, time.Now())
}
// We may want to allow short bursts of requests in
// our rate limiting scheme while preserving the
// overall rate limit. We can accomplish this by
// buffering our limiter channel. This `burstyLimiter`
// channel will allow bursts of up to 3 events.
burstyLimiter := make(chan time.Time, 3)
// Fill up the channel to represent allowed bursting.
for i := 0; i < 3; i++ {
burstyLimiter <- time.Now()
}
// Every 200 milliseconds we'll try to add a new
// value to `burstyLimiter`, up to its limit of 3.
go func() {
for t := range time.Tick(time.Millisecond * 200) {
burstyLimiter <- t
}
}()
// Now simulate 5 more incoming requests. The first
// 3 of these will benefit from the burst capability
// of `burstyLimiter`.
burstyRequests := make(chan int, 5)
for i := 1; i <= 5; i++ {
burstyRequests <- i
}
close(burstyRequests)
for req := range burstyRequests {
<-burstyLimiter
fmt.Println("request", req, time.Now())
}
}
執行上面程式碼,將得到以下輸出結果 -
F:\worksp\golang>go run rate-limiting.go
request 1 2017-01-21 14:43:39.1445218 +0800 CST
request 2 2017-01-21 14:43:39.345767 +0800 CST
request 3 2017-01-21 14:43:39.5460635 +0800 CST
request 4 2017-01-21 14:43:39.7441739 +0800 CST
request 5 2017-01-21 14:43:39.9444929 +0800 CST
request 1 2017-01-21 14:43:39.9464898 +0800 CST
request 2 2017-01-21 14:43:39.9504928 +0800 CST
request 3 2017-01-21 14:43:39.9544955 +0800 CST
request 4 2017-01-21 14:43:40.1467214 +0800 CST
request 5 2017-01-21 14:43:40.3469624 +0800 CST