You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vercel部署的NextJS应用中Server-Sent Events无法运行求助

解决Vercel部署NextJS流式聊天接口504超时问题

问题分析

本地运行正常但Vercel生产环境出现504,核心原因是Vercel Serverless Functions对长连接流式响应的限制:

  • 平台网关会因长时间未收到数据返回而触发超时
  • 默认的响应缓冲区未主动刷新,导致数据滞留在服务端
  • setInterval的并发执行可能引发资源竞争,延长响应时间

解决方案

1. 适配Vercel环境的响应头部

移除无效的Connection: keep-alive,添加分块传输头部,明确告知网关处理流式数据:

res.writeHead(200, {
  "Transfer-Encoding": "chunked",
  "Cache-Control": "no-cache, no-transform",
  "Content-Type": "text/event-stream",
});

2. 强制刷新响应缓冲区

每次调用res.write后执行res.flush(),确保数据立即发送到客户端,避免网关超时:

if (newMessages.length > 0) {
  lastTimestamp = newMessages[newMessages.length - 1].timestamp;
  res.write(`data: ${JSON.stringify(newMessages)}

`);
  await res.flush(); // 强制刷新缓冲区
}

3. 替换setInterval为递归setTimeout

避免setInterval在异步Redis请求未完成时重复触发,减少资源竞争:

const pollMessages = async () => {
  console.log("GETTING MESSAGES");
  try {
    const newMessages = await REDIS.getChatMessages({
      topic,
      fromTimestamp: lastTimestamp + 1,
    });

    console.log("NEW MESSAGES", newMessages);
    if (newMessages.length > 0) {
      lastTimestamp = newMessages[newMessages.length - 1].timestamp;
      res.write(`data: ${JSON.stringify(newMessages)}

`);
      await res.flush();
    }

    count += 1;

    if (count <= MAX_RUNS) {
      setTimeout(pollMessages, REFRESH_INTERVAL);
    } else {
      console.log("ENDING");
      res.end();
    }
  } catch (err) {
    console.error("POLL ERROR", err);
    res.end();
  }
};

// 启动轮询
pollMessages();

4. 优化连接关闭处理

简化关闭事件监听,避免重复调用res.end():

const cleanup = () => {
  res.end();
};

res.on("close", cleanup);
res.socket?.on("close", cleanup);

修改后的完整代码

// pages/api/chat.ts

import { NextApiRequest, NextApiResponse } from "next";

import { REDIS } from "@/serverApi";

const MAX_RUNS = 30;
const REFRESH_INTERVAL = 1000;

export const config = {
  maxDuration: 60,
};

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  console.log("STARTING");
  let lastTimestamp = Number(req.query.lastTimestamp) || 0;
  const topic = (req.query.topic as string) || "";

  let count = 0;

  res.writeHead(200, {
    "Transfer-Encoding": "chunked",
    "Cache-Control": "no-cache, no-transform",
    "Content-Type": "text/event-stream",
  });

  const cleanup = () => {
    res.end();
  };

  res.on("close", cleanup);
  res.socket?.on("close", cleanup);

  const pollMessages = async () => {
    console.log("GETTING MESSAGES");
    try {
      const newMessages = await REDIS.getChatMessages({
        topic,
        fromTimestamp: lastTimestamp + 1,
      });

      console.log("NEW MESSAGES", newMessages);
      if (newMessages.length > 0) {
        lastTimestamp = newMessages[newMessages.length - 1].timestamp;
        res.write(`data: ${JSON.stringify(newMessages)}

`);
        await res.flush();
      }

      count += 1;

      if (count <= MAX_RUNS) {
        setTimeout(pollMessages, REFRESH_INTERVAL);
      } else {
        console.log("ENDING");
        cleanup();
      }
    } catch (err) {
      console.error("POLL ERROR", err);
      cleanup();
    }
  };

  pollMessages();
};

export default handler;

额外注意事项

  • 确保Redis连接在Vercel环境下可正常访问,避免因网络问题导致请求阻塞
  • 若仍出现超时,可适当缩短MAX_RUNS或延长REFRESH_INTERVAL,减少函数运行时间
  • Vercel的Serverless Functions流式响应建议配合平台的Edge Functions使用,Edge Functions对长连接支持更好(若你的应用适配Edge环境)

内容的提问来源于stack exchange,提问作者user2634633

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 05:33:18