Ответ
Для ограничения количества одновременных (конкурентных) вызовов функции или обработчика в Go, не прибегая к сторонним библиотекам, можно использовать буферизованный канал или семафор.
Это не rate limiting (ограничение частоты), а concurrency limiting (ограничение параллелизма).
1. Использование буферизованного канала
Канал используется как семафор: размер буфера — это максимальное количество одновременных выполнений. Запись в канал "захватывает" слот, чтение — "освобождает".
2. Использование golang.org/x/sync/semaphore
Хотя этот пакет не входит в стандартную библиотеку, он является официальным подрепозиторием Go и предоставляет более гибкий и явный API для семафоров.
import (
"context"
"golang.org/x/sync/semaphore"
)
// Семафор с "весом" 10. Каждый запрос будет "весить" 1.
var sem = semaphore.NewWeighted(10)
func handler(w http.ResponseWriter, r *http.Request) {
// Пытаемся захватить семафор. Учитываем контекст запроса для отмены.
if err := sem.Acquire(r.Context(), 1); err != nil {
// Не удалось захватить (например, контекст отменен)
http.Error(w, "Failed to acquire semaphore", http.StatusServiceUnavailable)
return
}
// Освобождаем семафор после выполнения.
defer sem.Release(1)
// Логика обработки
time.Sleep(2 * time.Second)
w.Write([]byte("Request processed successfully"))
}
Буферизованный канал — простой и идиоматичный способ для базовых случаев. Семафор из x/sync
предоставляет больше контроля, например, возможность захвата с разным "весом" и интеграцию с context
.