Go by Example မြန်မာဘာသာ: Rate Limiting

Rate limiting ဆိုတာ resource အသုံးပြုမှုကို ထိန်းချုပ်ဖို့နဲ့ ဝန်ဆောင်မှုအရည်အသွေးကို ထိန်းသိမ်းဖို့ အရေးကြီးတဲ့ နည်းလမ်းတစ်ခု ဖြစ်ပါတယ်။ Go က goroutine တွေ၊ channel တွေ၊ နဲ့ ticker တွေကိုသုံးပြီး rate limiting ကို လှပစွာ ပံ့ပိုးပေးပါတယ်။

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

ပထမဆုံး rate limiting အခြေခံ ကို ကြည့်ကြပါမယ်။ ဝင်လာတဲ့ request တွေကို ကိုင်တွယ်တာကို ကန့်သတ်ချင်တယ် ဆိုကြပါစို့။ ဒီ request တွေကို တူညီတဲ့နာမည်ရှိတဲ့ channel တစ်ခုကနေ ဖြန့်ဝေပေးပါမယ်။

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

ဒီ limiter channel က 200 မီလီစက္ကန့်တိုင်း တန်ဖိုးတစ်ခု လက်ခံရရှိပါမယ်။ ဒါဟာ ကျွန်တော်တို့ရဲ့ rate limiting scheme မှာ regulator အဖြစ် ဆောင်ရွက်ပါတယ်။

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

Request တိုင်းကို မဖြန့်ဝေခင် limiter channel ကနေ လက်ခံတာကို block လုပ်ခြင်းအားဖြင့် ကျွန်တော်တို့ဟာ 200 မီလီစက္ကန့်တိုင်း request တစ်ခုသာ လုပ်ဆောင်နိုင်အောင် ကန့်သတ်ထားပါတယ်။

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

ကျွန်တော်တို့ရဲ့ rate limiting scheme မှာ ပျမ်းမျှ rate limit ကို ထိန်းသိမ်းထားပြီး တိုတောင်းတဲ့ request burst တွေကို ခွင့်ပြုချင်ပါတယ်။ ဒါကို limiter channel ကို buffer လုပ်ခြင်းဖြင့် လုပ်ဆောင်နိုင်ပါတယ်။ ဒီ burstyLimiter channel က 3 ခုအထိ burst ဖြစ်ခွင့်ပြုပါမယ်။

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

Bursting ခွင့်ပြုထားတာကို ကိုယ်စားပြုဖို့ channel ကို fill up လုပ်ပါမယ်။

    for i := 0; i < 3; i++ {
        burstyLimiter <- time.Now()
    }

200 မီလီစက္ကန့်တိုင်း burstyLimiter ထဲကို တန်ဖိုးအသစ်တစ်ခု ထည့်ဖို့ ကြိုးစားပါမယ်၊ သူ့ရဲ့ အများဆုံး 3 ခုဆိုတဲ့ ကန့်သတ်ချက်အထိပါ။

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

အခု နောက်ထပ် request 5 ခု ဝင်လာတာကို simulate လုပ်ပါမယ်။ ပထမ 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())
    }
}

ကျွန်တော်တို့ရဲ့ ပရိုဂရမ်ကို run လိုက်တဲ့အခါ ပထမအသုတ် request တွေကို ကျွန်တော်တို့ လိုချင်သလိုပဲ တစ်ခုနဲ့တစ်ခုကြား ~200 မီလီစက္ကန့်စီခြားပြီး handle လုပ်တာ တွေ့ရပါတယ်။

$ 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 တွေအတွက်တော့ ပထမဆုံး request သုံးခုကို burstable rate limiting ကြောင့် ချက်ချင်း ဖြေရှင်းပေးပါတယ်။ ပြီးတော့မှ ကျန်တဲ့ နှစ်ခုကို တစ်ခုနဲ့တစ်ခုကြား ~200ms စီ နှောင့်နှေးပြီး ဖြေရှင်းပေးပါတယ်။

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

နောက်ဥပမာ: Atomic Counters.