~/کدولاگ
→ بررسی اجمالی پروژه
یک REST API با محدودیت نرخ در Node.js

بخش ۲: یک Token Bucket

پیاده‌سازی الگوریتم token bucket در Redis تا محدودیت در همه‌ی نمونه‌های سرویس شما پابرجا بماند.

یک محدودکننده‌ی درون‌حافظه‌ای، به‌محض اجرای دو نمونه از سرویس از کار می‌افتد. سطل (bucket) را در Redis نگه می‌داریم تا هر نمونه یک منبع واحد از حقیقت را به اشتراک بگذارد.

الگوریتم

هر کلاینت یک سطل دارد که با نرخی ثابت تا یک سقف مشخص پر می‌شود. هر درخواست یک توکن هزینه دارد؛ اگر سطل خالی باشد، درخواست رد می‌شود.

async function allow(key: string): Promise<boolean> {
  const now = Date.now()
  const capacity = 20
  const refillPerSec = 5

  const [tokensStr, tsStr] = await redis.hmget(key, "tokens", "ts")
  let tokens = tokensStr ? Number(tokensStr) : capacity
  const last = tsStr ? Number(tsStr) : now

  tokens = Math.min(capacity, tokens + ((now - last) / 1000) * refillPerSec)

  if (tokens < 1) return false

  await redis.hmset(key, { tokens: tokens - 1, ts: now })
  return true
}

در محیط تولید، این را به‌صورت یک اسکریپت Lua اجرا کنید تا خواندن-تغییر-نوشتن اتمی باشد. دو درخواست همزمان هرگز نباید یک شمار توکن یکسان را ببینند.

نصب آن

app.use(async (req, res, next) => {
  if (await allow(`rl:${req.ip}`)) return next()
  res.status(429).json({ error: "rate limited" })
})

هدرهای Retry-After و X-RateLimit-Remaining را برگردانید تا کلاینت‌ها مؤدبانه عقب‌نشینی کنند. این پایان مجموعه است — یک محدودکننده‌ی توزیع‌شده در کمتر از ۴۰ خط.