Ответ
Rate Limiter (ограничитель скорости) — это механизм контроля частоты выполнения каких-либо операций, например, количества HTTP-запросов от одного клиента за определенный промежуток времени. Его основная цель — защита сервиса от перегрузки и злоупотреблений (DDoS, брутфорс).
Основные алгоритмы:
Token Bucket (Ведро с токенами)
Самый популярный алгоритм. "Ведро" имеет определенную емкость (burst
) и пополняется токенами с фиксированной скоростью (rate
). Каждый запрос "забирает" один токен. Если токенов нет — запрос отклоняется или ожидает.Leaky Bucket (Дырявое ведро)
Запросы помещаются в очередь (ведро). Они обрабатываются с постоянной скоростью, как вода, вытекающая через отверстие. Если очередь переполнена, новые запросы отбрасываются. Сглаживает пики запросов.Fixed Window Counter (Счетчик в фиксированном окне)
Простой алгоритм. Счетчик запросов обнуляется в начале каждого временного окна (например, каждую минуту). Минус: возможен всплеск нагрузки на стыке окон (например, двойной лимит за короткое время).Sliding Window Log (Скользящее окно)
Более сложный и точный. Хранит временные метки запросов и учитывает только те, что попадают в текущее скользящее окно. Решает проблему всплесков на границах, но требует больше памяти.
Реализация в Go:
Стандартным решением является пакет golang.org/x/time/rate
, который реализует алгоритм Token Bucket.
import (
"context"
"fmt"
"time"
"golang.org/x/time/rate"
)
func main() {
// Лимитер: 2 запроса в секунду, с "запасом" в 5 запросов.
limiter := rate.NewLimiter(rate.Limit(2), 5)
// Неблокирующая проверка
for i := 0; i < 10; i++ {
if limiter.Allow() {
fmt.Println("Request allowed")
} else {
fmt.Println("Request denied")
}
time.Sleep(200 * time.Millisecond)
}
// Блокирующая проверка (ожидает, пока токен не станет доступен)
ctx := context.Background()
fmt.Println("nWaiting for token...")
if err := limiter.Wait(ctx); err == nil {
fmt.Println("Token acquired, request can proceed.")
}
}
Распределенные системы:
Для нескольких экземпляров сервиса локальные лимитеры неэффективны. В таких случаях используют централизованное хранилище (например, Redis) для хранения счетчиков, реализуя один из алгоритмов на стороне хранилища. Популярные библиотеки: go-redis/redis_rate
.