使用EventSource对接Vercel无服务器函数时出现客户端504网关超时
Vercel无服务器函数上EventSource间歇性504网关超时问题解决
本地运行EventSource连接Next.js API路由(模拟MongoDB变更流推送)完全正常,但部署到Vercel后,连接间歇性返回504网关超时,即便已配置超时参数且客户端连接清理逻辑正确,问题仍存在。
服务端代码
import { NextApiRequest, NextApiResponse } from "next"; export default async function handler(req: NextApiRequest, res: NextApiResponse) { res.setHeader("Content-Type", "text/event-stream"); res.setHeader("Cache-Control", "no-cache"); res.setHeader("Connection", "keep-alive"); res.writeHead(200); res.flushHeaders(); let intervalId: NodeJS.Timeout; try { console.log("Change stream initialized"); intervalId = setInterval(() => { const simulatedData = { timestamp: new Date().toISOString(), message: "This is a simulated message", }; res.write(`data: ${JSON.stringify(simulatedData)}\n\n`); res.flush(); }, 10000); req.on("close", () => { console.log("Client disconnected"); clearInterval(intervalId); res.end(); }); } catch (error) { console.error("Error initializing change stream:", error); res.status(500).end(); } }
客户端代码
import { useEffect } from "react"; function MyComponent() { useEffect(() => { const eventSource = new EventSource(`/api/getdata`); eventSource.onmessage = (event) => { const data = JSON.parse(event.data); console.log('Received update:', data); }; eventSource.onerror = (error) => { console.error('EventSource error:', error); eventSource.close(); }; return () => { eventSource.close(); console.log('EventSource closed'); }; }, []); return <div>Listening for updates...</div>; }
问题根源
Vercel普通无服务器函数(Serverless Functions)有最大执行时间限制(上限10秒),你的代码设置每10秒发送一次数据,函数执行时长会超过这个限制,导致Vercel主动终止连接,返回504超时。另外,普通无服务器函数是为短生命周期请求设计的,不支持长连接场景(如EventSource)。
解决方案
1. 切换到Vercel Edge Functions(推荐)
Edge Functions运行在边缘节点,支持长连接,无严格执行时间限制(只要连接活跃)。修改API路由配置:
- 若使用Pages Router,在
pages/api的函数文件中添加运行时配置:
export const config = { runtime: "edge", };
- 调整服务端代码适配Edge Functions(改用流式响应):
import { NextRequest, NextResponse } from "next/server"; export const config = { runtime: "edge", }; export default async function handler(req: NextRequest) { const stream = new ReadableStream({ start(controller) { const intervalId = setInterval(() => { const simulatedData = { timestamp: new Date().toISOString(), message: "This is a simulated message", }; controller.enqueue(`data: ${JSON.stringify(simulatedData)}\n\n`); }, 10000); // 监听客户端断开事件,清理资源 req.signal.addEventListener("abort", () => { clearInterval(intervalId); controller.close(); }); }, }); return new NextResponse(stream, { headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive", }, }); }
2. 临时缓解:缩短数据发送间隔
如果暂时无法切换到Edge Functions,可将数据发送间隔缩短至10秒以内(如5秒),确保函数在Vercel超时限制内完成执行。但这只是临时方案,无法解决长连接的根本问题。
3. 第三方实时服务替代
若Edge Functions仍不满足需求,可使用Pusher、Supabase Realtime等第三方服务处理MongoDB变更流推送,无需自行维护长连接。
验证步骤
- 部署Edge Functions后,查看Vercel控制台函数日志,确认连接保持活跃且无超时终止
- 保留客户端原有EventSource逻辑,确保组件卸载时正确关闭连接
内容的提问来源于stack exchange,提问作者Devesh Maurya
相关产品推荐
相关产品推荐

