Go با مثال: محدودسازی نرخ

محدودسازی نرخ (rate limiting) سازوکار مهمی برای کنترل مصرف منابع و حفظ کیفیت سرویس است. Go با گوروتین‌ها، کانال‌ها و تیکرها به شکلی زیبا از محدودسازی نرخ پشتیبانی می‌کند.

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

اول محدودسازی نرخ ساده را می‌بینیم. فرض کنید می‌خواهیم رسیدگی به درخواست‌های ورودی را محدود کنیم. این درخواست‌ها را از کانالی به همین نام سرویس می‌دهیم.

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

کانال limiter هر ۲۰۰ میلی‌ثانیه یک مقدار دریافت می‌کند. این تنظیم‌کنندهٔ طرح محدودسازی نرخ ماست.

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

با مسدود شدن روی دریافت از کانال limiter پیش از سرویس دادن به هر درخواست، خودمان را به ۱ درخواست در هر ۲۰۰ میلی‌ثانیه محدود می‌کنیم.

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

ممکن است بخواهیم در طرح محدودسازی نرخ، اجازهٔ رگبارهای کوتاهی از درخواست‌ها (burst) را بدهیم و در عین حال محدودیت نرخ کلی حفظ شود. این کار را می‌توان با بافردار کردن کانال limiter انجام داد. کانال burstyLimiter اجازهٔ رگبارهایی تا ۳ رویداد را می‌دهد.

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

کانال را پر می‌کنیم تا رگبار مجاز را نشان دهد.

    for range 3 {
        burstyLimiter <- time.Now()
    }

هر ۲۰۰ میلی‌ثانیه سعی می‌کنیم یک مقدار جدید به burstyLimiter اضافه کنیم، تا سقف ۳.

    go func() {
        for t := range time.Tick(200 * time.Millisecond) {
            burstyLimiter <- t
        }
    }()

حالا ۵ درخواست ورودی دیگر را شبیه‌سازی می‌کنیم. ۳ درخواست اول از قابلیت رگبارِ 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())
    }
}

با اجرای برنامه می‌بینیم که همان‌طور که می‌خواستیم، به دستهٔ اول درخواست‌ها هر ~۲۰۰ میلی‌ثانیه یک بار رسیدگی می‌شود.

$ 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

در دستهٔ دوم درخواست‌ها، به لطف محدودسازی نرخِ رگبارپذیر، به ۳ درخواست اول فوراً رسیدگی می‌شود و سپس ۲ درخواست باقی‌مانده هر کدام با ~۲۰۰ms تأخیر.

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

مثال بعدی: .