Контекст: В backend/internal/middleware/ratelimit.go используется in-memory token bucket (golang.org/x/time/rate). В main.go:54 создаётся глобальный лимитер на 60 req/min на IP.
In-memory rate limiter не работает в production при:
// backend/cmd/api/main.go:54
rl := middleware.NewRateLimiter(rate.Limit(60), 60) // In-memory!
r.Use(rl.Middleware())
// backend/internal/middleware/ratelimit.go (упрощённо)
type RateLimiter struct {
limiter *rate.Limiter // In-memory token bucket
}
Использовать Redis с sliding window / token bucket алгоритмом. Ключ: ratelimit:{ip}:{endpoint} или ratelimit:{user_id}:{endpoint}.
github.com/ulule/limiter/v3 — поддерживает Redis, in-memory, sliding windowimport (
"github.com/redis/go-redis/v9"
"github.com/ulule/limiter/v3"
"github.com/ulule/limiter/v3/drivers/store/redis"
)
store := redis.NewStoreWithOptions(redisClient, limiter.StoreOptions{
Prefix: "ratelimit",
})
rate := limiter.Rate{Period: 1*time.Minute, Limit: 60}
instance := limiter.New(store, rate)
| Эндпоинт | Лимит | Ключ |
|---|---|---|
/auth/* |
10/min | IP |
GET /places |
60/min | UserID или IP |
POST /places |
10/min | UserID |
/admin/* |
100/min | UserID |
| Подход | Плюсы | Минусы |
|---|---|---|
| Redis + ulule/limiter (рекомендую) | Работает в кластере, гибкие правила | Зависимость от Redis |
| nginx rate limiting | На уровне инфраструктуры | Меньше гибкости, сложнее per-user |
| Cloudflare / API Gateway | Не трогает код | Vendor lock-in, платное |
Задача: Code review PhotoPlaces — rate limiter не работает в проде
#backend #rate-limiting #redis #scaling #critical #best-practice