英文源地址
速率控制式控制资源和保持服务质量的重要机制.Go优雅地支持用goroutine, channel和tickers实现速率限制.
package main
import (
"fmt"
"time"
)
func main() {
// 首先我们来看看基本的速率限制.
// 假设我们想要处理对传入请求的限制.
// 我们将通过同名的通道来处理这些请求.
requets := make(chan int, 5)
for i := 1; i <= 5; i++ {
requets <- i
}
close(requets)
// 此limiter通道将每200毫秒接受一个值,这是我们速率限制方案中的调节器
limiter := time.Tick(200 * time.Millisecond)
// 通过在服务每个请求之前阻塞在limiter通道上, 我们将自己限制在每200毫秒处理一个请求.
for req := range requets {
<-limiter
fmt.Println("request", req, time.Now())
}
// 我们可能希望在速率限制方案中允许短的突发请求, 同时保留整体的速率限制.
// 我们可以通过带缓冲的limiter通道来实现这一点.
// 这个burstyLimiter通道允许最多3个事件的爆发.
burstyLimiter := make(chan time.Time, 3)
// 填满通道以表示允许爆发.
for i := 0; i < 3; i++ {
burstyLimiter <- time.Now()
}
// 每隔200毫秒, 我们尝试向burstyLimiter添加一个新值, 直到它的3的限制
go func() {
for t := range time.Tick(200 * time.Millisecond) {
burstyLimiter <- t
}
}()
// 限制再模拟传入5个请求
// 前3个将受益于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())
}
}
运行我们的程序, 我们看到第一批请求按预期每200毫秒处理一次.
对于第二批请求, 由于突发速率的限制,我们立即服务了前3个请求, 然后服务其余2个请求, 每个请求延迟约200ms.
$ go run rate-limiting.go
request 1 2012-10-19 00:38:18.687438 +0000 UTC
request 2 2012-10-19 00:38:18.887471 +0000 UTC
request 3 2012-10-19 00:38:19.087238 +0000 UTC
request 4 2012-10-19 00:38:19.287338 +0000 UTC
request 5 2012-10-19 00:38:19.487331 +0000 UTC
request 1 2012-10-19 00:38:20.487578 +0000 UTC
request 2 2012-10-19 00:38:20.487645 +0000 UTC
request 3 2012-10-19 00:38:20.487676 +0000 UTC
request 4 2012-10-19 00:38:20.687483 +0000 UTC
request 5 2012-10-19 00:38:20.887542 +0000 UTC
下一节将介绍: 原子计数器.