Skip to main content

Netlify Function 消耗超限:Next.js 在 Netlify 上的真实消耗机制分析

问题背景

在将 Next.js 多语言歌词网站从 Vercel 迁移到 Netlify 后,遇到了 Function Invocations 消耗超限的问题。免费额度为 125,000 次/月,但实际消耗远超预期,导致运行不流畅。

实际发现的问题

关键发现:即使访问静态页面也消耗 Function

// 实际测试结果
访问模式:
- 访问首页(静态页面)→ 仍然消耗 1Function
- 访问歌词页面(静态页面)→ 仍然消耗 1Function
- 访问静态资源 → 消耗 Function
- 重定向请求 → 消耗 1Function
- Link 预读取 → 可能消耗 Function(如果触发页面渲染)

月访问量:~50,000
Function 消耗:50,000 × 10 = 500,000
实际消耗:~600,000 次(包含重定向、中间件和预读取)

问题根源:Next.js 在 Netlify 上的运行机制

// 实际观察到的现象
{
"静态页面访问": {
"预期": "不消耗 Function",
"实际": "消耗 1 次 Function",
"原因": "Next.js 在 Netlify 上的处理机制"
},
"动态页面访问": {
"预期": "消耗 1 次 Function",
"实际": "消耗 1 次 Function",
"符合预期": true
},
"中间件处理": {
"预期": "每个请求消耗 Function",
"实际": "每个请求消耗 Function",
"符合预期": true
}
}

具体消耗场景分析

1. 中间件消耗(确认的问题)

// 问题代码:middleware.ts
export function middleware(request: NextRequest) {
// 每个请求都消耗 1 次 Function 调用
const locale = getLocale(request);
const pathname = request.nextUrl.pathname;

if (pathname.startsWith(`/${locale}`)) {
return NextResponse.next();
}

return NextResponse.redirect(new URL(`/${locale}${pathname}`, request.url));
}

实际消耗

  • 每个页面访问:1 次 Function 调用
  • 每个静态资源访问:1 次 Function 调用
  • 重定向请求:1 次 Function 调用

2. 静态页面访问消耗(意外发现)

// 即使页面是静态生成的,访问时仍消耗 Function
// 这是 Next.js 在 Netlify 上的特殊行为

访问静态页面:
- 构建时:页面已预生成(约1,890个静态页面)
- 访问时:仍然通过 Function 处理
- 消耗:1Function 调用

3. 重定向消耗(确认的问题)

// 重定向逻辑消耗 Function
重定向场景:
- 根路径重定向到 /en:1Function 调用
- 语言检测重定向:1Function 调用
- 404 重定向:1Function 调用

解决方案

方案1:删除中间件,使用 Netlify 重定向规则

// 删除 middleware.ts,改用 _redirects 文件
# _redirects
/ /en 302
/:path* /en/:path* 302

实际效果

  • 重定向不再消耗 Function
  • 静态重定向更高效
  • Function 消耗减少 30%
// 禁用 Link 预读取,避免触发 Function
// 项目中共有 14 个 Link 组件设置了 prefetch={false}

<Link href="/page" prefetch={false}>
Page Link
</Link>

// 预读取在 Netlify 上可能触发 Function 调用
// 禁用后可以避免不必要的 Function 消耗

效果

  • 避免预读取触发 Function
  • 减少 Function 消耗
  • 按需加载,提升用户体验

方案3:最大化静态生成(但效果有限)

// 即使完全静态化,访问时仍消耗 Function
export async function generateStaticParams() {
const locales = ['en', 'zh', 'es', 'fr'];
const songs = await getAllSongs();

return locales.flatMap(locale =>
songs.map(song => ({
locale,
songSlug: song.slug
}))
);
}

实际效果

  • 页面在构建时生成(约1,890个静态页面)
  • 但访问时仍消耗 Function(Netlify 特性)
  • Function 消耗减少有限

方案4:迁移回 Vercel(最终选择)

// 基于实际测试结果,选择迁移回 Vercel
原因:
- Vercel 静态页面访问不消耗 CPU
- Netlify 静态页面访问仍消耗 Function
- Vercel 更适合nextjs的静态化项目

优化效果对比

优化前(Netlify)

Function 消耗统计:
- 页面访问:50,000
- 中间件处理:50,000
- 重定向:10,000
- 静态资源加载:450,000
- 总计:560,000

月消耗:600,000
免费额度:125,000
超负荷:+475,000

优化后(迁移回 Vercel)

CPU 消耗统计:
- 静态页面访问:0 次(Vercel 特性)
- 静态重定向:0 次(vercel.json 配置)
- 中间件处理:0 次(已删除)
- 总计:0

月消耗:0 小时
免费额度:4小时/
剩余:4小时/

总体效果

  • Function 消耗减少 100%(迁移到 Vercel)
  • 页面加载速度提升 60%
  • 成本大幅降低

关键发现和教训

1. 平台差异的重要性

// 不同平台的消耗机制差异
Vercel:
- 静态页面访问:不消耗 CPU
- 动态页面访问:消耗 CPU
- 适合:静态化项目

Netlify:
- 静态页面访问:仍消耗 Function
- 动态页面访问:消耗 Function
- 适合:动态功能多的项目

2. 静态化的局限性

// 静态化在不同平台的效果
Vercel 静态化:
- 构建时生成页面
- 访问时不消耗资源
- 效果:显著

Netlify 静态化:
- 构建时生成页面
- 访问时仍消耗 Function
- 效果:有限

3. 平台选择的重要性

// 基于项目特点选择平台
静态内容多的项目:
- 推荐:Vercel
- 原因:静态页面不消耗资源

动态功能多的项目:
- 推荐:Netlify
- 原因:Function 计算时间充足

最佳实践建议

1. 测试平台差异

// 部署前测试不同平台的消耗
测试步骤:
1. 部署到 Vercel,监控 CPU 消耗
2. 部署到 Netlify,监控 Function 消耗
3. 对比实际消耗数据
4. 选择最适合的平台

2. 避免不必要的中间件

// ❌ 避免:在中间件中处理静态资源
export function middleware(request: NextRequest) {
// 这会为每个静态资源请求消耗 Function
if (request.nextUrl.pathname.startsWith('/_next/')) {
return NextResponse.next();
}
}

// ✅ 推荐:使用平台配置
# _redirects (Netlify)
/_next/static/* /_next/static/:splat 200

# vercel.json (Vercel)
{
"headers": [
{
"source": "/_next/static/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
}
]
}

3. 合理选择平台

// 平台选择决策树
if (项目类型 === "静态内容多") {
if (流量 < 100GB/) {
选择: "Vercel"
} else {
选择: "自建服务器 + CDN"
}
} else if (项目类型 === "动态功能多") {
if (Function 调用 < 125,000/) {
选择: "Netlify"
} else {
选择: "自建服务器"
}
}

总结

Netlify Function 消耗超限问题的关键发现:

  1. 静态页面访问仍消耗 Function:这是 Next.js 在 Netlify 上的特殊行为
  2. 中间件过度使用:每个请求都消耗 Function
  3. 平台差异显著:不同平台的消耗机制差异很大

解决方案

  • 删除不必要的中间件
  • 使用平台特定的重定向规则
  • 基于项目特点选择合适的平台

最终选择:nextjs项目迁移回 Vercel,因为静态页面访问不消耗 CPU,更适合静态化项目。其他框架项目可以考虑继续使用Netlify。

关键教训:平台选择不是技术问题,而是成本问题。需要基于实际测试数据选择最适合的平台,而不是盲目迁移。