Goによる例: レート制限

レート制限は、リソース利用を制御し、サービス品質を維持するための重要なメカニズムです。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ミリ秒ごとに、3という制限まで、burstyLimiterに新しい値を追加しようとします。

    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

2番目のバッチのリクエストでは、バースト可能なレート制限により最初の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

次の例: アトミックカウンター.