Go за Прикладом: Обмеження Частоти Запитів

Work in Progress / Сайт в процесі розробки

Обмеження Частоти Запитів важливий механізм контролю за ресурсом, його використовують для підтримки необхідної якості роботи (і обмеження навантаження з сторонніх джерел). Go дозволяє гарно реалізувати обмеження частоти за допомоги горутин, каналів та маятників.

package main
import "time"
import "fmt"
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 мс і оброблятимето по одному запиту за цей період.

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

Можливо, ми схочемо дозволяти невеликі “напливи” запитів, вцілому, притримуючись нашої стандартної схеми обмеженя. Досягнути цього ми можемо створивши буфер в нашому каналі “обмеження”. Цей канал burstyLimiter дозволятиме напиливи аж трьох подій.

    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, який блокує доступ для 4-того та 5-того запитів, аж до моменту коли сам канал “обмежувач” не буде знову наповнено.

    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 мілісекунд.

$ 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

Друга ж партія, представляє собою три запити що не обмежуються (це “наплив” - дозволена кількіть без обмеження) та ще два - додаткових, що обмежені 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

Наступний приклад: Атомарні Лічильники.