Ограничение частоты запросов в Go
Одним из факторов при проектировании приложения является ожидаемый объём трафика от пользователей. Объём трафика влияет на распределение ресурсов, масштабирование и стоимость хостинга.
Ограничение частоты запросов — одна из техник контроля входящего трафика к приложению или сети.
Что такое ограничение частоты запросов?
Ограничение частоты запросов — распространённый способ ограничить сетевой трафик в пределах заданных временных окон или при превышении числа запросов от пользователя.
Эта техника полезна для снижения риска атак, таких как брутфорс и DDoS (распределённый отказ в обслуживании), ограничивает web scraping, нагрузку на API и другие нежелательные автоматизированные взаимодействия (боты).
В Go для реализации rate limiting используется пакет rate, который совместим с пакетом time.
Пакет rate является частью экосистемы Go (golang.org/x/time/rate), но не входит в стандартную библиотеку. Его нужно добавить в зависимости проекта.
Выполните эту команду в терминале рабочей директории, чтобы добавить пакет:
go get "golang.org/x/time/rate"Импортируйте пакеты в файл Go для этого примера:
import (
"encoding/json"
"golang.org/x/time/rate"
"log"
"net/http"
)Пакет json используется для кодирования структуры в JSON для клиента. Пакет log нужен для логирования ошибок в консоль, а http — для создания endpoint, middleware и запуска сервера.
Простое API с одним endpoint
Обычно для ограничения частоты запросов пишут middleware, который оборачивает обработчик. Каждый раз при поступлении запроса middleware проверяет, разрешён ли доступ, и в случае успеха передаёт управление обработчику.
Вот модель структуры с полями-строками, которую мы кодируем в JSON:
type Message struct {
Response string `json:"response"`
Description string `json:"description"`
}Обработчик устанавливает Content-Type = application/json, возвращает статус 200 и кодирует экземпляр структуры в ответ.
func endpointExample(writer http.ResponseWriter, request *http.Request) {
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(http.StatusOK)
message := Message{
Response: "Successful",
Description: "You've successfully hit the API endpoint",
}
err := json.NewEncoder(writer).Encode(&message)
if err != nil {
return
}
}Функция endpointExample принимает writer и request из пакета http и возвращает JSON с помощью writer.
Ограничение частоты запросов в простом приложении на Go
Ограничение можно реализовать по числу запросов в единицу времени или по доступному «кредиту» запросов (burst). Всегда нужно создать лимитер до авторизации запроса.
Ниже пример создания лимитера и авторизации по числу запросов:
func rateLimiterMiddleware(next func(writer http.ResponseWriter, request *http.Request)) http.HandlerFunc {
limiter := rate.NewLimiter(3, 6) // max of 6 requests and then three more requests per second
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
if !limiter.Allow() {
writer.Write([]byte("rate limit exceeded "))
return
} else {
endpointExample(writer, request)
}
})
}Функция rateLimiterMiddleware — middleware, принимающая обработчик и создающая лимитер через rate.NewLimiter. Первый параметр — скорость (events/секунду), второй — burst (максимум перед началом регулярной конверсии).
Метод Allow возвращает булево значение — разрешён ли текущий запрос. В примере при превышении лимита клиент получает текст “rate limit exceeded”.
func main() {
http.HandleFunc("/home", rateLimiterMiddleware(endpointExample))
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Println("There was an error listening on port :8080", err)
}
}Функция main монтирует endpoint /home на middleware и запускает сервер на localhost:8080.
Для тестирования можно выполнить в терминале:
for i in {1..10}; do curl http://localhost:8080/home; doneЭтот цикл выполнит 10 запросов к /home. На шестом запросе, в примере с burst=6, последующие запросы начнут получать отказ.
Когда ограничение запросов не сработает
Важно: rate limiting — не универсальное решение. Он не защитит, если:
- Атака идёт с большого количества распределённых адресов (если лимит привязан к IP, понадобятся дополнительные меры).
- Злоумышленник использует легитимные учётные записи (требуется поведение на уровне приложения — throttling по пользователю, капча, MFA).
- Нехватка ресурсов на сервере уже произошла (ограничение запросов помогает предотвратить, но не мгновенно восстановить систему).
Альтернативные и дополняющие подходы
- Сниффинг и фильтрация на уровне CDN/WAF (Cloudflare, AWS WAF) — блокировка до попадания на приложение.
- Rate limiting на уровне API gateway (например, Kong, Envoy) — централизованное управление.
- Token bucket и leaky bucket — разные алгоритмы для разных сценариев;
rateв Go фактически реализует token bucket. - Трёхуровневая защита: CDN → API gateway → приложение.
Эмпирические правила и эвристики
- Начинайте с консервативных лимитов в тестовом окружении.
- Используйте burst для пиковых легитимных операций, но держите его небольшим.
- Привязывайте лимиты не только к IP: по ключу API, аккаунту, пользователю и даже по маршруту.
- Логируйте отклонённые запросы отдельно для анализа.
Важно: выбор привязки лимита (IP, user, API key) определяет, какие типы атак вы сможете отразить.
Уровни зрелости внедрения
- Уровень 1 — базовый: глобальный лимит на endpoint (как в примере).
- Уровень 2 — продвинутый: лимиты по API-ключам или пользователям, маршрутам.
- Уровень 3 — корпоративный: интеграция с CDN/WAF, мониторинг, автоматическое масштабирование и alerting.
Факто-бокс
- Пример в статье: скорость = 3 req/s, burst = 6.
- Типичный порядок тестов: нагрузка в 2–10× ожидаемой пиков.
- Пакет: golang.org/x/time/rate.
Мини-методология внедрения
- Оцените сценарии использования и ожидаемую нагрузку.
- Выберите стратегию привязки лимитов (IP, user, api key).
- Реализуйте локально middleware с логированием отказов.
- Протестируйте в staging с повторяемыми нагрузочными тестами.
- Перенесите на prod с мониторингом и alertami.
Чек‑лист по ролям
Разработчик:
- Реализовать middleware и unit-тесты.
- Обеспечить корректные заголовки ответа и коды статуса.
DevOps:
- Настроить метрики и дашборды (ошибки 429, latency).
- Выбрать место для rate limiting (app, gateway, CDN).
Security:
- Определить правила блокировки и реагирования на аномалии.
- Проверить, что лимит не позволяет легко обходить защиту.
Критерии приёмки
- Система отказывает запросам при превышении лимита (код 429 или понятный ответ).
- Логируются все отклонённые запросы.
- Нельзя обойти лимит простым переадресованием трафика.
Тестовые сценарии
- Выполнить цикл из 10 запросов и убедиться, что после burst запросы отклоняются.
- Тесты для разных ключей API и комбинаций IP.
- Нагрузочный тест для проверки влияния на latency.
Диаграмма принятия решения
flowchart TD
A[Наблюдается повышенная нагрузка] --> B{Это легитимный трафик?}
B -- Да --> C[Увеличить масштаб/проверить burst]
B -- Нет --> D{IP распределён?}
D -- Да --> E[Применить блокировку на уровне CDN/WAF]
D -- Нет --> F[Ограничить по ключу API/пользователю]
C --> G[Мониторинг]
E --> G
F --> GБезопасность и конфиденциальность
- Логи отказов не должны содержать чувствительных данных (пароли, токены).
- При хранении метрик соблюдайте требования локального законодательства о данных.
Краткое резюме
Ограничение частоты запросов — важная базовая приёмка для защиты и экономии ресурсов. В Go пакет golang.org/x/time/rate даёт простой и гибкий способ реализовать token bucket. Для надёжной защиты комбинируйте лимиты с уровнями CDN и API gateway.
Глоссарий
- rate limiting — ограничение количества операций в единицу времени.
- burst — кратковременное превышение скорости (буфер для пиков).
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone