🌊 为什么要"限流"?
想象一个场景:你辛辛苦苦搭建的 Next.js API 服务,正沐浴在阳光下稳定运行,忽然来了一个过分勤劳的客户端用户(或机器人 🤖),开始毫无节制地请求接口,疯狂到让 CPU 开始思考人生。
结果自然是:
💥「服务崩溃 → 用户暴躁 → 老板追杀」
于是我们需要"限流机制"(Rate Limiting)。
限流的本质,就是在 单位时间内限制某个用户或 IP 的请求次数,从而保持服务稳定。例如:
| 指标 | 意义 | 
|---|---|
| 10 req / 10 s | 每 10 秒最多处理 10 个请求 | 
| 100 req / min | 每分钟处理 100 个请求 | 
| 429 状态码 | 拒绝请求并提示"停一下!" | 
🧩 Upstash 与 Redis ------ 天作之合
我们不想自己去维护复杂的 Redis 集群来追踪请求计数,于是 Upstash 这个小精灵出现了:
- ☁️ 无服务器 Redis(Serverless Redis)
- 💸 按调用计费,不用担心多一分钱的浪费
- ⚡️ 延迟低,速度快
- 🔐 免费额度够个人项目用
而 @upstash/ratelimit 就是它的魔法棒,让限流在 Next.js 中轻松实现。
🚀 开始动手:Next.js + @upstash/ratelimit
下面我们构造一个限流且带审计日志的 API。
1️⃣ 安装依赖
            
            
              bash
              
              
            
          
          npm install @upstash/ratelimit @upstash/redis2️⃣ 创建 Redis 客户端
在 /lib/redis.js:
            
            
              arduino
              
              
            
          
          import { Redis } from "@upstash/redis";
export const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL,
  token: process.env.UPSTASH_REDIS_REST_TOKEN,
});🔑 安全提示: 环境变量千万别放到 GitHub 上,除非你想给别人白送额度。
3️⃣ 初始化 RateLimiter
在 /lib/ratelimit.js:
            
            
              javascript
              
              
            
          
          import { Ratelimit } from "@upstash/ratelimit";
import { redis } from "./redis";
export const ratelimit = new Ratelimit({
  redis,
  limiter: Ratelimit.slidingWindow(5, "10 s"), // 每10秒最多允许5次请求
  analytics: true, // 启用分析数据
  prefix: "ratelimit:api",
});🔍 审计日志:记录谁在"频繁敲门"
当请求超限时,我们顺带写入日志。
日志可以简单保存在 Redis,也可以后续接入到像 Datadog、OpenTelemetry 等监控系统。
在 /pages/api/hello.js:
            
            
              javascript
              
              
            
          
          import { ratelimit } from "../../lib/ratelimit";
import { redis } from "../../lib/redis";
export default async function handler(req, res) {
  const ip = req.headers["x-real-ip"] || req.socket.remoteAddress || "unknown";
  const { success, limit, reset, remaining } = await ratelimit.limit(ip);
  if (!success) {
    await redis.rpush("audit:rate_limit_log", {
      ip,
      time: new Date().toISOString(),
      path: req.url,
    });
    return res.status(429).json({
      message: "⛔ 太快啦,稍等一下再试!",
      limit,
      reset,
      remaining,
    });
  }
  res.status(200).json({ message: `✅ 欢迎,来自 ${ip} 的合格请求!` });
}📈 小试牛刀:查看日志
只要登录 Redis 查看我们写入的日志:
            
            
              ini
              
              
            
          
          const logs = await redis.lrange("audit:rate_limit_log", 0, -1);
console.log("🚨 限流日志记录:", logs);输出示例:
            
            
              lua
              
              
            
          
          [
  { ip: '203.0.113.1', time: '2025-10-31T06:00:00Z', path: '/api/hello' },
  { ip: '203.0.113.1', time: '2025-10-31T06:00:02Z', path: '/api/hello' },
  ...
]📜 小贴士:你也可以用 Cloudflare KV、Supabase 或 PostgreSQL 来保存日志,用于更强大的审计分析。
🧠 一点底层思考:Rate Limit 的本质
限流算法背后其实就像"水桶原理"一样。常见的策略有:
- 🪣 固定窗口(Fixed Window) :简单粗暴,每个时间窗限固定次数。
- ⏳ 滑动窗口(Sliding Window) :更平滑,统计窗口"滑动"过去一段时间请求。
- 💧 令牌桶(Token Bucket) :每次请求取一个"令牌",定期补充。
- 💦 漏桶(Leaky Bucket) :请求流入"桶",系统按固定速率漏出处理。
@upstash/ratelimit 默认提供了"滑动窗口",兼顾性能与公平性。
🖼️ 可视化(脑内小剧场)
下面是一幅简单的比喻图:
            
            
              arduino
              
              
            
          
          ┌──────────┐       请求 x5 次/10秒
│ Client 🌍│ ─────┐
└──────────┘      │
                   ▼
             ┌──────────┐
             │ Ratelimit │ ------ "5次够了!去喝杯茶☕"
             └──────────┘
                   ▼
             ┌──────────┐
             │ Redis 🧠 │ ← 记录日志 & 限流计数
             └──────────┘🧾 总结
| 项目 | 内容 | 
|---|---|
| 功能 | 限制接口调用速率、防止滥用 | 
| 库 | @upstash/ratelimit,@upstash/redis | 
| 审计 | Redis 保存请求日志 | 
| 应用场景 | API 接口、Webhooks、防刷票系统 | 
| 优点 | 简洁、轻量、Serverless 友好 | 
🎁 拓展玩法(进阶食谱)
- 多维度限流:不仅按 IP,还可以按用户 ID、API key、Referer 等。
- 图表可视化审计:用 Next.js/Chart.js 展示限流日志的趋势。
- 智能警告系统:超过阈值时触发 Slack/Email 通知管理员。
- 动态限流:高峰期更严,闲时更松。