Vercel部署踩坑系列3:日志排查
到了这一步了,终于在Vercel上部署成功,以为万事无忧了,结果网站打不开......
已经濒临崩溃,果然新手在Vercel上部署,一步三个坑。没办法,继续排查吧。
排查步骤
1. 检查 Vercel 环境变量
在 Vercel 项目设置中确认是否已正确配置:
DATABASE_URL="postgresql://user:password@host:port/database"
AUTH_SECRET="your-secret-key"
还有所有上线要准备的东西,尤其数据库、管理员账号等,是否都已经正确配置并初始化。
2. 检查 Vercel 部署日志
上面还是自排查,这步检查日志基本能找到所有问题了。在 Vercel 控制台查看详细错误:
- 进入项目
- 选择最新部署
- 点进去查看 "Logs"
这一看果然发现了问题:
错误 1:国际化文件缺失
ENOENT: no such file or directory, open '/var/task/src/i18n/messages/zh.json'
错误 2:数据库连接问题
Can't reach database server at `db.ssowepbajeuvawlxsmog.supabase.co:5432`
先来解决错误1:
检查了一下,文件是存在的,那么真相就剩下一个: Vercel 在打包时没有正确包含这些 JSON 文件。
在项目根目录的 next.config.ts 中添加配置,确保 JSON 文件被包含:
import type { NextConfig } from 'next';
import createNextIntlPlugin from 'next-intl/plugin';
const withNextIntl = createNextIntlPlugin();
const nextConfig: NextConfig = {
// 确保 i18n 消息文件被正确复制到输出目录
webpack: (config, { isServer }) => {
if (isServer) {
config.externals = config.externals || [];
}
return config;
},
// 或者使用 experimental.outputFileTracingIncludes
experimental: {
outputFileTracingIncludes: {
'/': ['./src/i18n/messages/**/*'],
},
},
};
export default withNextIntl(nextConfig);
还不放心,在代码里面直接动态导入,修改 request.ts::
export async function loadMessages(locale: Locale) {
// 改用动态导入而不是文件系统读取
const messages = await import(`./messages/${locale}.json`);
return nest(messages.default);
}
再来看错误2:
了解了一下为什么连不上,因为我当前使用的是 Direct Connection (直连模式,端口 5432),在 Vercel 的 Serverless 环境中经常超时,所以要改成Transaction pooler(连接池模式,端口 6543)
- 登录 Supabase
- 进入项目
- 点击 Connect,切换模式复制连接地址
然后在 Vercel 环境变量中更新 DATABASE_URL:
postgresql://postgres.[PROJECT-REF]:[PASSWORD]@db.ssowepbajeuvawlxsmog.supabase.co:6543/postgres?pgbouncer=true
查了一下为什么会超时,因为在 Vercel 的 Serverless 环境中:
- 每次请求都建立新的数据库连接
- Vercel Functions 是短生命周期的,不适合直接连接,而端口 5432 用于长期连接
三种模式对比:
| 类型 | 端口 | 适用场景 | Vercel 是否推荐 |
|---|---|---|---|
| Direct connection | 5432 | 长期运行的服务器 | 不推荐会超时(适合本地需要连接云数据库的时候) |
| Session pooler | 6543 | Serverless 环境 | 比较推荐 |
| Transaction pooler | 6543 | Serverless 环境 | 首推 |
全部搞完后松了一口气以为问题解决了,部署上去一看日志又来一个问题:
prepared statement "s0" already exists
又是一顿搜索,发现这是因为使用了上面 Transaction pooler 的问题,但是PgBouncer 不支持 Prisma 的 prepared statements......
幸好还是有解决方案的,在 Vercel 环境变量中,在 DATABASE_URL 末尾添加 &pgbouncer=true&statement_cache_size=0
pgbouncer=true- 告诉 Prisma 正在使用 pgbouncerstatement_cache_size=0- 禁用 prepared statement 缓存
最后大功告成,页面终于出来了!
相关系列: