添加Upstash Rate Limiter后NextJS API调用耗时增至5秒求排查
我跟着教程在自研的Next.js API GET路由上配置了Upstash Rate Limiter,代码基本沿用官方示例。未添加限流时,API调用耗时约50ms;添加限流后,耗时直接增至约5秒。
无限流代码:
import Albums from "@models/albums"; import { connectToDB } from "@utils/database"; import { NextResponse } from 'next/server' export const GET = async () => { try { await connectToDB() const albums = await Albums.find({}) return NextResponse.json( albums, { status: 200 } ) } catch (error) { console.log(error) return NextResponse.json( "Failed to fetch all ablums", { status: 500 } ) } }
添加限流后的代码:
import Albums from "@models/albums"; import { connectToDB } from "@utils/database"; import { Ratelimit } from "@upstash/ratelimit"; import { Redis } from "@upstash/redis"; import { NextResponse } from 'next/server' const ratelimit = new Ratelimit({ redis: Redis.fromEnv(), limiter: Ratelimit.slidingWindow(1, "10 s") }) export const GET = async (request) => { try { const ip = request.headers.get("x-forwarded-for") ?? ""; const { success, reset } = await ratelimit.limit(ip); if (!success) { const now = Date.now(); const retryAfter = Math.floor((reset - now) / 1000); return new NextResponse("Too Many Requests", { status: 429, headers: { ["retry-after"]: `${retryAfter}`, }, }); } await connectToDB() const albums = await Albums.find({}) return NextResponse.json( albums, { status: 200 } ) } catch (error) { console.log(error) return NextResponse.json( "Failed to fetch all ablums", { status: 500 } ) } }
可能的原因及解决方案:
Redis实例与部署区域不匹配
如果你用的Upstash Redis实例和Next.js部署的服务器不在同一区域,跨区域网络请求会产生极高延迟。比如Next.js部署在北美,Redis实例在新加坡,单次Redis调用可能就需要几秒。
解决:登录Upstash控制台查看Redis实例的区域,调整为和Next.js部署区域一致;本地测试时可以暂时用固定IP测试,排除区域影响。Serverless函数冷启动开销
Next.js的Serverless函数在冷启动时,会重新初始化Redis客户端和Ratelimit实例,额外增加连接时间。如果你的API请求量低,函数经常被回收,每次请求都要重新建立Redis连接,耗时自然会涨。
解决:保持函数热状态(比如定时触发请求);如果场景允许,改用Next.js Edge Runtime,Edge环境的冷启动速度远快于Serverless;另外你已经把Ratelimit实例初始化放在函数外部,这已经是最优做法了。Upstash实例性能限制
免费版Upstash Redis有带宽、请求频率等限制,当请求触发这些限制时,会被限流导致延迟飙升。
解决:去Upstash控制台查看实例的监控面板,检查是否有请求超时、限流的记录;如果是免费版,可以考虑升级到付费版测试。IP获取逻辑的潜在问题
你用x-forwarded-for获取IP,但如果这个头不存在或者格式异常,会用空字符串作为限流key。虽然代码里做了兜底,但空key可能导致Redis内部处理异常,或者触发不必要的逻辑开销。
解决:暂时把IP换成固定值(比如const ip = "test-ip"),测试耗时是否下降,排除这个因素。依赖版本兼容性问题
@upstash/ratelimit、@upstash/redis和Next.js版本不兼容,可能导致内部逻辑异常,增加额外延迟。
解决:查看Upstash官方文档,确认推荐的Next.js版本,尝试升级或降级相关依赖到兼容版本。
内容的提问来源于stack exchange,提问作者ShortyLeKid

