← Назад к вопросам
Rate Limiter (Token Bucket)
2.0 Middle🔥 251 комментариев
#Конкурентность и горутины#Основы Go#Производительность и оптимизация
Условие
Реализуйте алгоритм ограничения запросов по принципу корзины токенов (Token Bucket Rate Limiter).
Требования
- Токены пополняются с фиксированной скоростью
- Максимальный запас токенов ограничен фиксированным значением
- За каждый вызов нужно вычитать N токенов
- Если токенов недостаточно, возвращается false
Сигнатура
type RateLimiter struct {
// ваши поля
}
func NewRateLimiter(rate float64, capacity int) *RateLimiter
func (r *RateLimiter) Allow() bool
func (r *RateLimiter) AllowN(n int) bool
Пример
limiter := NewRateLimiter(10.0, 100) // 10 токенов/сек, макс 100
limiter.Allow() // true если есть токен
limiter.AllowN(5) // true если есть 5 токенов
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение
Token Bucket Rate Limiter — это алгоритм для ограничения частоты запросов. Используется в системах ограничения API, защиты от DDoS, и контроля нагрузки. Ключевая идея: токены пополняются с фиксированной скоростью, каждый запрос расходует токены.
Принцип работы
- Инициализация: в корзине максимум
capacityтокенов - Пополнение: каждую секунду добавляется
rateтокенов (но не болееcapacity) - Проверка доступа: если токенов достаточно, вычитаем их и разрешаем запрос
- Отказ: если токенов недостаточно, отказываем в запросе
Реализация
import (
"sync"
"time"
)
type RateLimiter struct {
mu sync.Mutex
rate float64
capacity float64
tokens float64
lastTime time.Time
}
func NewRateLimiter(rate float64, capacity int) *RateLimiter {
return &RateLimiter{
rate: rate,
capacity: float64(capacity),
tokens: float64(capacity),
lastTime: time.Now(),
}
}
func (r *RateLimiter) refill() {
now := time.Now()
elapsed := now.Sub(r.lastTime).Seconds()
r.lastTime = now
r.tokens += elapsed * r.rate
if r.tokens > r.capacity {
r.tokens = r.capacity
}
}
func (r *RateLimiter) Allow() bool {
return r.AllowN(1)
}
func (r *RateLimiter) AllowN(n int) bool {
r.mu.Lock()
defer r.mu.Unlock()
r.refill()
if r.tokens >= float64(n) {
r.tokens -= float64(n)
return true
}
return false
}
Пример использования
func main() {
limiter := NewRateLimiter(10.0, 100)
for i := 0; i < 100; i++ {
if limiter.Allow() {
println("Request", i, "allowed")
}
}
if !limiter.Allow() {
println("Request rejected: no tokens")
}
time.Sleep(1 * time.Second)
for i := 0; i < 10; i++ {
if limiter.Allow() {
println("After wait - request allowed")
}
}
}
Ключевые моменты
- Mutex: потокобезопасность
- Lazy refill: пополнение только при вызове Allow()
- Floating-point: точные расчёты времени
- Времени последнего обновления: для корректного расчёта прошедшего времени
Сложность
- Временная: O(1)
- Пространственная: O(1)