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

Next.js 14部署Vercel时SSE报错:任务15秒后超时

问题:Vercel部署SSE应用超时且重连失败

我正在开发一款使用Server-Side Events(SSE)流式传输Twitch账号名至显示屏,以切换/展示Twitch嵌入组件的应用。本地运行正常,但部署到Vercel后出现错误:Task timed out after 15.01 seconds,且客户端无法重连,错误持续出现。

代码片段

Route.ts

export async function GET() {
  const encoder = new TextEncoder();
  let screenId: string;

  const cookieStore = cookies();
  const TokenCookie = cookieStore.get("Token");

  if (!TokenCookie) {
    return new Response("Token not found", { status: 401 });
  }

  try {
    const Token = await verify(TokenCookie.value);
    const TokenPayload = JSON.parse(Token.payload as string);

    screenId = TokenPayload.id;
    console.log("Token ID:", screenId);
  } catch (error) {
    console.error("Error verifying token:", error);
    return new Response("Invalid token", { status: 403 });
  }
  
  const customReadable = new ReadableStream({
    async start(controller) {
      redisSubscriber.subscribe(setKey, (err) => {
        if (err) {
          console.error("Error subscribing to Redis:", err);
          controller.error(err); // Signal an error in the stream
          return;
        }
      });

      redisSubscriber.on("message", (channel, message) => {

        const parsed = JSON.parse(message);
        if (channel === setKey && parsed.screenId === screenId) {
          controller.enqueue(encoder.encode(`data: ${message}\n\n`));
        } else {
          console.log("Invalid token, ignoring message.");
        }
      });

      redisSubscriber.on("error", (error) => {
        console.error("Redis error:", error);
        controller.error(error); 
      });
    }

  });

  return new Response(customReadable, {
    headers: {
      "Content-Type": "text/event-stream; charset=utf-8",
      "Connection": "keep-alive",
      "Cache-Control": "no-cache",
      "Content-Encoding": "none",
    },
  });
}

page.tsx

useEffect(() => {
    let eventSource = new EventSource("/api/stream");

    eventSource.addEventListener("message", (event) => {
      try {
        const streamData = JSON.parse(event.data);

        if (streamData.stream) {
          setTwitchChannel(streamData.stream);
        }
        if (streamData.events) {
          setEvents(streamData.events);
        }

        if (streamData.fullscreen && streamData.fullscreen === true) {

          setTimeout(() => {
            router.push(`/screen/fullscreen/?channel=${twitchChannel}`);
          }, 0);


        }
      } catch (error) {
        console.error("Error parsing JSON:", error);
      }
    });

    eventSource.addEventListener("error", () => {
      eventSource.close();
      setTimeout(() => {
        eventSource =new EventSource("/api/stream");
      }, 1000);
    });
  }, [router, twitchChannel, events]);

已尝试的方案

我尝试过设置runtime为edge/node/serverless、移除Redis等方法,也参考了大量SSE代码示例,但均无效。另外因体积过大无法使用Edge runtime。

解决思路与建议

  • 核心原因:Vercel Serverless函数超时限制
    Vercel Serverless函数默认最大执行时间是15秒,这就是你遇到Task timed out错误的直接原因。SSE需要维持长连接,而Serverless函数的设计是短请求处理,不适合长期持有连接,到时间就会被强制终止。

  • 调整SSE连接策略
    在SSE流中定期发送心跳包(比如每10秒发送一次data: \n\n),虽然不能突破Vercel的最大执行时间限制(最高90秒,依计划而定),但能避免连接被提前判定为空闲断开。

  • 改用支持长连接的部署方式
    放弃Serverless函数,改用Vercel的Container部署(自定义运行环境,支持长连接),或者选择其他原生支持SSE/WebSocket长连接的平台(如Fly.io、Railway)。

  • 修复Redis订阅资源泄漏
    当前代码中Redis订阅在函数结束后不会被取消,会导致资源泄漏。需要在ReadableStream的cancel方法中添加取消订阅的逻辑:

    const customReadable = new ReadableStream({
      async start(controller) {
        // 原订阅逻辑...
      },
      async cancel() {
        redisSubscriber.unsubscribe(setKey);
        redisSubscriber.removeAllListeners("message");
        redisSubscriber.removeAllListeners("error");
      }
    });
    
  • 优化客户端重连逻辑
    把固定1秒重试改成指数退避策略(比如1秒、2秒、4秒,最大到30秒),避免频繁请求触发Vercel的限流机制:

    let retryDelay = 1000;
    eventSource.addEventListener("error", () => {
      eventSource.close();
      setTimeout(() => {
        eventSource = new EventSource("/api/stream");
        retryDelay = Math.min(retryDelay * 2, 30000);
      }, retryDelay);
    });
    
  • 简化Token验证流程
    确保Token验证逻辑尽可能快速完成,避免因这部分耗时过长提前触发函数超时。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 16:00:54