Ответ
Для ограничения частоты запросов к API (rate limiting) в Go чаще всего используют пакет golang.org/x/time/rate
. Он реализует алгоритм "token bucket" (метод токенов в ведре).
Основная идея — есть "ведро" с определенной емкостью, которое пополняется токенами с фиксированной скоростью. Каждый запрос "забирает" один токен. Если токенов нет, запрос блокируется или отклоняется.
Пример с golang.org/x/time/rate
:
import (
"net/http"
"golang.org/x/time/rate"
)
// Создаем limiter: 1 токен в секунду, с "ведром" на 5 токенов.
// Это позволяет обрабатывать короткие всплески до 5 запросов.
var limiter = rate.NewLimiter(1, 5)
// Middleware для проверки ограничения
func limitMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Allow() проверяет, можно ли взять токен. Если нет, возвращает false.
if !limiter.Allow() {
http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
return
}
next.ServeHTTP(w, r)
})
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
})
// Оборачиваем наш обработчик в middleware
http.ListenAndServe(":8080", limitMiddleware(mux))
}
Распределенные системы
Для систем, работающих на нескольких инстансах, локальный limiter
не подойдет, так как каждый инстанс будет иметь свой собственный счетчик. В таких случаях состояние лимитера (количество токенов) выносят во внешнее хранилище, например, Redis, используя атомарные операции для управления счетчиками.