Go by Example: Rate Limiting

レートリミット (Rate limiting) は、リソースの使用量を制御し、サービスの品質を維持するための、 とても重要な仕組みです。 Go は、ゴルーチンとチャネル、ティッカー により、 レートリミットをエレガントにサポートします。

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

まず最初に、基本的なレートリミットを見ていきます。 リクエストに対する処理を制限したいとしましょう。 これらのリクエストに同じ名前の requests チャネルで対応します。

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

あるいは、全体としてはレートリミットを担保しつつ、 一時的なバーストリクエストは許容したいと思うかもしれません。 その場合、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 ミリ秒に 1 回処理されていることが分かります。

$ 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 ミリ秒ずつ遅れて実行されていることが 分かります。3 つまではバーストを許容しているからです。

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

Next example: Atomic Counters.