Go by Example:速率限制

速率限制 是用於控制資源使用量,並維持服務品質的重要機制。Go 語言以協程、頻道和 計時器 優雅地支援了速率限制。

package main
import (
    "fmt"
    "time"
)
func main() {

首先,我們將探討基本速率限制。假設我們想要限制處理 incoming request。我們會建立一個擁有相同名稱的頻道,使用它來提供這些 request 的服務。

    requests := make(chan int, 5)
    for i := 1; i <= 5; i++ {
        requests <- i
    }
    close(requests)

這個 limiter 頻道每 200 毫秒就會接收一個值。這是我們的速率限制機制中的調節器。

    limiter := time.Tick(200 * time.Millisecond)

在為每個 request 提供服務之前,阻擋來自 limiter 頻道的接收,我們就能讓自己每 200 毫秒處理一個 request。

    for req := range requests {
        <-limiter
        fmt.Println("request", req, time.Now())
    }

我們或許會想要在速率限制機制中允許簡短地 burst request,同時維持總體速率限制。我們可以透過緩衝 limiter 頻道來達成這個目標。這個 burstyLimiter 頻道將允許 burst 最多 3 個事件。

    burstyLimiter := make(chan time.Time, 3)

填滿頻道以表示允許 burst。

    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 個 incoming request。前 3 個將會受惠於 burstyLimiter 的 burst 能力。

    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())
    }
}

執行我們的程式,我們會看到第一批 request 在約每 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

對於第二批 request,由於享有爆發性速率限制,我們可以立即提供前 3 個服務,再以約 200 毫秒的延遲,提供其餘 2 個服務。

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

下一篇範例:原子櫃檯