Go в примери: Ограничителни ставки

Ограничаването на скоростта на обработка на входящите данни1 е важно за управлението на използваните ресурси и поддръжка на качеството на доставяните услуги. Го подхожда изкусно чрез съвместна употреба на канали, гозадачи и часовници.


  1. rate limiting – ограничаване на размера, броя или скоростта за единица време, задаване на ставка.
package main
import (
    "fmt"
    "time"
)
func main() {

Първо ще разгледаме най-простия начин за ограничаване на скоростта. Да предположим, че искаме да ограничим обработката на входящите заявки. Ще обслужваме тези заявки по канал със същото име.

    заявки := make(chan int, 5)
    for i := 1; i <= 5; i++ {
        заявки <- i
    }
    close(заявки)

Каналът ограничител ще произвежда по една стойност на всеки 200 милисекунди. Това е регулаторът в нашето устройство за ограничаване на скоростта.

    ограничител := time.Tick(200 * time.Millisecond)

Програмата спира хода си, защото чака стойност от канала часовник ограничител. Така ограничаваме програмата да обработва най-много една заявка от канала заявки на всеки 200 милисекунди.

    for req := range заявки {
        време := <-ограничител
        fmt.Println("заявка",
            req, време.Format(`15:04.000`))
    }

Може да искаме да разрешим кратки резки покачвания на броя на заявките, като същевременно поддържаме цялостното ограничение. Можем да постигнем това, като задържаме данни в канала ограничител. Каналът разрешаващСкок ще позволява скокове до три събития.

    разрешаващСкок := make(chan time.Time, 3)

Да напълним канала за скок на заявките.

    for range 3 {
        разрешаващСкок <- time.Now()
    }

На всеки 200 милисекунди ще добавяме нова стойност в разрешаващСкок, докато достигнем ограничението 3.

    go func() {
        for t := range time.Tick(200 * time.Millisecond) {
            разрешаващСкок <- t
        }
    }()

Сега наподобяваме още 5 новопристигащи заявки. Три от тях ще се възползват от възможността за скок в обработката на заявките, разрешен от ограничителНаСкока.

    скочилиЗаявки := make(chan int, 5)
    for i := 1; i <= 5; i++ {
        скочилиЗаявки <- i
    }
    close(скочилиЗаявки)
    for req := range скочилиЗаявки {
        t := <-разрешаващСкок
        fmt.Println("заявки",
            req, t.Format(`15:04.000`))
    }
}

Като пуснем програмата виждаме, че заявките от първата поредица биват обработени по една на 200 милисекунди, както искахме.

$ go run rate-limiting.go
заявка 1 10:35.357
заявка 2 10:35.557
заявка 3 10:35.757
заявка 4 10:35.957
заявка 5 10:35.157

При втората поредица от заявки, обслужваме първите три незабавно, понеже сме разрешили скок на ставката. Следващите две обработваме със забавяне от по 200 милисекунди за всяка.

заявки 1 10:35.158
заявки 2 10:35.158
заявки 3 10:35.158
заявки 4 10:35.358
заявки 5 10:35.558

Следващ пример: .