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

如何在线托管支持Server Side Events的SvelteKit应用?

如何在线托管基于SSE(EventSource)的应用?

问题背景

我近期完成了一款模拟罗伯特议事规则的应用,通过JavaScript的EventSource实现客户端事件触发,本地运行表现完美,但部署到线上后遇到多平台适配问题。

客户端代码片段

onMount(() => {
        const source = new EventSource(`/rooms/${roomId}/activity`, {
            withCredentials: false
        });
        const event = SSEvents[roomId as keyof typeof SSEvents];
        source.addEventListener(event, (e) => {
            const message = JSON.parse(e.data);
            const membername: string = message.name;
            delete message.name;
            if (message.type === 'set') {
                messageStore.update(($messageStore) => $messageStore.set(membername, message));
                if (membername === $user?.toString()) {
                    selectedValue = message.status;
                }
            }
            if (message.type === 'delete') {
                messageStore.update(($messageStore) => {
                    $messageStore.delete(membername);
                    return $messageStore;
                });
            }
        });
        return () => {
            source.close();
        };
    });

线上部署问题

  • Vercel平台:触发504“超时”错误,推测是事件流在产生变更前超时。
  • Cloudflare平台:EventSource持续显示在调试菜单的网络面板中,但内容为空。
  • Netlify平台:返回502“网关错误”。

解决方案

1. Vercel平台适配

Vercel普通无服务器函数默认10秒超时,无法维持SSE长连接,需调整为:

  • 改用Edge Functions,它支持最长60秒的连接时长,适配流式响应。示例代码:
    export const config = { runtime: 'edge' };
    export default async function handler(req) {
      const { roomId } = req.params;
      const headers = new Headers({
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
      });
    
      const stream = new ReadableStream({
        async start(controller) {
          // 定时发送心跳包,避免超时断开
          const heartbeatInterval = setInterval(() => {
            controller.enqueue('data: {"type":"heartbeat"}\n\n');
          }, 5000);
    
          // 监听连接关闭信号,清理资源
          req.signal.addEventListener('abort', () => {
            clearInterval(heartbeatInterval);
            controller.close();
          });
    
          // 这里添加房间事件监听逻辑,有更新时推送数据
        },
      });
    
      return new Response(stream, { headers });
    }
    
  • 强制发送心跳包:每隔5-10秒推送空事件或心跳数据,防止负载均衡器断开空闲连接。

2. Cloudflare平台适配

Cloudflare的缓存和压缩机制会干扰SSE流,需做以下调整:

  • 后端响应头添加:Cache-Control: no-cache, no-store, must-revalidate、CF-Cache-Status: DYNAMIC,强制跳过缓存。
  • 关闭Cloudflare控制台中"Speed"选项下的"Auto Minify"对text/event-stream类型的压缩。
  • 若使用Cloudflare Workers,确保Worker直接转发流式响应,不缓冲完整内容后再发送。

3. Netlify平台适配

Netlify普通无服务器函数同样有10秒超时限制,适配方案:

  • 改用Netlify Edge Functions,支持流式响应和更长连接时长。
  • 或拆分部署:Netlify仅托管前端静态资源,将SSE服务部署到独立后端(如虚拟机、容器服务),前端直接连接该后端地址。

4. 通用最佳实践

  • 必加心跳机制:无业务数据时,每隔5-10秒发送data: heartbeat\n\n,避免反向代理/CDN断开连接。
  • 规范响应头:后端必须返回正确的SSE响应头:
    Content-Type: text/event-stream
    Cache-Control: no-cache, no-store, must-revalidate
    Connection: keep-alive
    
  • 优先选择长连接友好的托管方式:避免用普通无服务器函数托管SSE服务,优先选虚拟机、容器服务(如Docker部署到ECS、Cloud Run)。
  • 实现客户端自动重连:监听error事件,断开后自动重试:
    function initSSE() {
      const source = new EventSource(`/rooms/${roomId}/activity`, { withCredentials: false });
      
      // 原有事件监听逻辑...
      
      source.onerror = () => {
        source.close();
        setTimeout(initSSE, 3000); // 3秒后重连
      };
      return source;
    }
    const sseSource = initSSE();
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 21:45:03