速率限制是控制资源利用和维持服务质量的重要机制。通过goroutines
,channel
和ticker
都可以优雅地支持速率限制。
首先我们来看一下基本速率限制。假设想限制对传入请求的处理。我们会在相同名称的通道上放送这些要求。
这个限制器通道将每200
毫秒接收一个值。这是速率限制方案中的调节器。
通过在服务每个请求之前阻塞来自限制器信道的接收,我们限制自己每200
毫秒接收1
个请求。
我们可能希望在速率限制方案中允许短脉冲串请求,同时保持总体速率限制。可以通过缓冲的限制器通道来实现。这个burstyLimiter
通道将允许最多3
个事件的突发。
填充通道以表示允许突发。
每200
毫秒,将尝试向burstyLimiter
添加一个新值,最大限制为3
。现在模拟5
个更多的传入请求。这些传入请求中的前3
个未超过burstyLimiter
值。
运行程序后,就会看到第一批请求每〜200
毫秒处理一次。
对于第二批请求,程序会立即服务前3
个,因为突发速率限制,然后剩余2
服务都具有〜200ms
延迟。
所有的示例代码,都放在
F:\worksp\golang
目录下。安装Go编程环境请参考:http://www.yiibai.com/go/go_environment.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