Rate Limiting в Go (Golang): библиотеки, паттерны и примеры реализации
Rate limiting (ограничение скорости запросов) — один из ключевых механизмов защиты и контроля нагрузки в современных веб-сервисах и микросервисной архитектуре. В экосистеме Go (Golang) существует несколько зрелых инструментов для реализации rate limiting, от встроенного пакета time до специализированных библиотек, таких как golang.org/x/time/rate. В этой статье мы подробно разберём, как работает rate limiting в Go, какие библиотеки использовать и приведём практические примеры кода.
Что такое Rate Limiting и зачем он нужен?
Rate limiting — это техника контроля количества запросов, которые клиент может выполнить к серверу за определённый промежуток времени. Основные цели:
- Защита от DDoS-атак — предотвращение перегрузки сервера.
- Честное распределение ресурсов — ограничение "прожорливых" клиентов.
- Соблюдение SLA — гарантия доступности для всех пользователей.
- Экономия затрат — снижение нагрузки на базы данных и внешние API.
В Go rate limiting реализуется с помощью алгоритмов Token Bucket (ведро токенов) и Leaky Bucket (дырявое ведро). Наиболее популярен Token Bucket.
Библиотека golang.org/x/time/rate
Основная и рекомендуемая библиотека для rate limiting в Go — это golang.org/x/time/rate. Она реализует алгоритм Token Bucket и входит в расширенный набор пакетов Go (sub-repositories).
Установка:
go get golang.org/x/time/rate
Основные компоненты
rate.Limiter— основной объект ограничителя.rate.NewLimiter(r rate.Limit, b int)— создаёт лимитер с частотойrзапросов в секунду и размером ведраb.Allow()— проверяет, можно ли выполнить запрос сейчас (неблокирующая).Wait(ctx)— блокирует выполнение, пока не появится доступный токен (учитывает контекст).Reserve()— возвращаетReservation, позволяющий отложить выполнение.
Пример 1: Базовый rate limiter
package main
import (
"fmt"
"golang.org/x/time/rate"
"time"
)
func main() {
// 2 запроса в секунду, ведро на 5 токенов
limiter := rate.NewLimiter(2, 5)
for i := 0; i < 10; i++ {
if limiter.Allow() {
fmt.Printf("Запрос %d выполнен\n", i)
} else {
fmt.Printf("Запрос %d отклонён (лимит)\n", i)
}
time.Sleep(200 * time.Millisecond)
}
}
Пример 2: Rate limiter с ожиданием (Wait)
package main
import (
"context"
"fmt"
"golang.org/x/time/rate"
"time"
)
func main() {
limiter := rate.NewLimiter(1, 3) // 1 запрос в секунду, ведро на 3
ctx := context.Background()
for i := 0; i < 6; i++ {
err := limiter.Wait(ctx)
if err != nil {
fmt.Println("Ошибка ожидания:", err)
return
}
fmt.Printf("Запрос %d выполнен в %s\n", i, time.Now().Format("15:04:05.000"))
}
}
Реализация HTTP middleware с rate limiting
На практике rate limiting чаще всего применяется на уровне HTTP-сервера. Рассмотрим пример middleware для Gin или стандартного net/http.
package main
import (
"net/http"
"golang.org/x/time/rate"
)
// RateLimiterMiddleware возвращает middleware с rate limit
func RateLimiterMiddleware(r rate.Limit, b int) func(http.Handler) http.Handler {
limiter := rate.NewLimiter(r, b)
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !limiter.Allow() {
http.Error(w, "429 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: 10 запросов в секунду, ведро на 20
handler := RateLimiterMiddleware(10, 20)(m