在线文档  >   Golang练习   >   速率限制

速率限制,是控制资源利用和维护服务质量的重要机制。Go使用协程、通道和定时器优雅地支持速率限制。

package main

import (
    "fmt"
    "time"
)

func main() {

    // 首先我们来看基本的速率限制。
    // 假设我们想限制对传入请求的处理,我们将从同名的通道中服务这些请求。
    requests := make(chan int, 5)
    for i := 1; i <= 5; i++ {
        requests <- i
    }
    close(requests)

    // 这个 `limiter` 通道每200毫秒接收一个值。这是我们速率限制方案中的调节器。
    limiter := time.Tick(200 * time.Millisecond)

    // 通过在每个请求之前从 `limiter` 通道接收来阻塞,我们将自己限制为每200毫秒处理1个请求。
    for req := range requests {
        <-limiter
        fmt.Println("request", req, time.Now())
    }

    // 我们可能希望在保持整体速率限制的情况下允许短时间突发请求。
    // 我们可以通过缓冲速率限制器通道来实现这一点。
    // 这个 `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 毫秒处理一次。
$ 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

#对于第二批请求,由于可突发的速率限制,我们立即为前 3 个请求提供服务,然后为剩余的 2 个请求提供服务,每个请求延迟 ~200 毫秒。
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