API 速率限制:演算法與實作

Security2026-09-20TryQuickToolBox

您的 API 正遭受攻擊。不是來自精密的駭客,而是一個簡單的腳本,每秒鐘數千次地猛擊您的端點,消耗資源並降低合法用戶的服務品質。如果沒有速率限制,一個行為不當的客戶端就可能拖垮整個應用程式。本指南說明核心演算法,並展示在 Node.js 和 Nginx 中的實用實作,以保護您的 API。

為何速率限制至關重要

速率限制控制客戶端在給定時間窗口內可以發出的請求數量。它是您抵禦以下威脅的第一道防線:

除了安全性之外,速率限制可確保公平使用,並協助您執行業務規則,例如分級定價方案。

速率限制演算法

四種演算法主導了 API 速率限制。每種在準確性、記憶體用量和突發處理方面都有取捨。

1. 固定窗口

在固定時間間隔內計算請求數(例如每分鐘 100 個請求)。當窗口重置時,計數器也會重置。

2. 滑動窗口

追蹤每個請求的時間戳,並計算在過去 N 秒內有多少個請求。這可以平滑突發流量。

3. 令牌桶

一個桶子裝有令牌。令牌以固定速率添加。每個請求消耗一個令牌。如果桶子空了,請求就會被拒絕。

4. 漏桶

請求進入佇列(桶子),並以恆定速率處理。如果佇列已滿,請求就會被丟棄。

演算法突發處理記憶體使用案例
固定窗口差低簡單 API
滑動窗口好中通用
令牌桶極佳中允許突發的 API
漏桶無中平滑流量

在 Node.js 中實作速率限制

我們將使用 Express 和 Redis 實作一個令牌桶速率限制器,以實現分散式狀態。當您有多個伺服器執行個體時,Redis 至關重要。

步驟 1:安裝依賴項目

npm install express redis

步驟 2:建立速率限制中介軟體

const redis = require('redis');
const client = redis.createClient();

async function tokenBucketLimiter(req, res, next) {
  const key = `rate_limit:${req.ip}`;
  const capacity = 10; // max tokens
  const refillRate = 1; // tokens per second

  const now = Date.now();
  const data = await client.hGetAll(key);
  let tokens = data.tokens ? parseFloat(data.tokens) : capacity;
  let lastRefill = data.lastRefill ? parseInt(data.lastRefill) : now;

  // Refill tokens based on elapsed time
  const elapsed = (now - lastRefill) / 1000;
  tokens = Math.min(capacity, tokens + elapsed * refillRate);

  if (tokens < 1) {
    return res.status(429).json({ error: 'Too many requests' });
  }

  tokens -= 1;
  await client.hSet(key, {
    tokens: tokens.toString(),
    lastRefill: now.toString()
  });
  await client.expire(key, 60); // auto-cleanup

  next();
}

app.use(tokenBucketLimiter);

這個中介軟體會原子性地檢查並更新令牌數量。在生產環境中,請使用 Redis 交易或 Lua 指令碼來避免競爭條件。

在 Nginx 中實作速率限制

Nginx 透過 limit_req 模組提供內建的速率限制。它使用漏桶演算法。

步驟 1:定義速率限制區域

在 nginx.conf 的 http 區塊中:

limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

這會建立一個名為 api 的 10MB 區域,允許每個 IP 每秒 10 個請求。

步驟 2:套用限制

在您的 location 區塊中:

location /api/ {
    limit_req zone=api burst=20 nodelay;
    proxy_pass http://backend;
}

burst=20 允許最多 20 個請求的短暫突發。nodelay 會立即處理突發請求,而不是將它們排入佇列。

API 速率限制的最佳實踐

常見問題

速率限制與節流有何不同?

速率限制會封鎖超過閾值的請求,而節流會減慢它們的速度(例如透過佇列或延遲)。速率限制是二元式的;節流是漸進式的。

我應該使用哪種速率限制演算法?

對於大多數 API,令牌桶提供了良好的平衡:它允許突發,但強制執行平均速率。如果您需要嚴格的平滑處理,請使用漏桶。為了簡單起見,固定窗口適用於低流量 API。

如何為已驗證與匿名使用者處理速率限制?

對匿名使用者(例如按 IP)套用更嚴格的限制,對已驗證使用者(例如按 API 金鑰)套用更寬鬆的限制。您也可以根據訂閱方案實作分層限制。

準備好分析您的 API 流量了嗎?使用我們的 Nginx Log Analyzer 來解析日誌、找出速率限制違規並最佳化您的閾值。