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

使用Firebase Rewrites时Cloud Run API服务响应异常问题

Firebase Rewrite代理Cloud Run流式API返回502/504的解决办法

直接访问Cloud Run的URL时,SvelteKit客户端和服务器API都运行正常;但通过Firebase Hosting的Rewrite规则用vc-ticker.web.app访问时,客户端页面功能正常,API的POST请求却返回502或504错误。Cloud Run日志显示已接收请求并返回ReadableStream,但该流数据从未到达客户端。

当前配置

firebase.json

{
  "hosting": {
    "public": "public",   // 未使用,应用由Cloud Run托管
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "run": {
          "serviceId": "vc-ticker-app",
          "region": "us-central1"
        }
      }
    ]
  }
}

客户端流式请求代码(+page.svelte)

const logging = true;
const controller = new AbortController();
let reader = null;
const signal = controller.signal;

async function streamer(params) {
  console.log("stream with logging:", logging, JSON.stringify(params));
  try {
    const response = await fetch("api/my-ticker", {
      method: "POST",
      body: JSON.stringify(params),
      headers: {
        "content-type": "application/json",
      },
      signal: signal,
    });

    const stream = response.body.pipeThrough(new TextDecoderStream("utf-8"));
    reader = stream.getReader();

    while (true) {
      const { value, done } = await reader.read();

      if (done || response.status !== 200) {
        console.log("done response", response.status, done, value);
        await reader.cancel(`reader done or invalid response: ${response.status}`);
        reader = null;
        break;
      }

      // 将多块JSON解析为数组后更新store
      const quotes = {};
      JSON.parse(`[${value.replaceAll("}{", "},{")}]`).forEach((each, idx) => {
        quotes[each.id] = [each.price, each.changePercent];
        console.log(`quote-${idx}:`, quotes[each.id]);
      });
      positions.set(quotes);
    }
  } catch (err) {
    console.log("streamer exception", err.name, err);
    if (reader) {
      await reader.cancel(`client exception: ${err.name}`);
      reader = null;
    }
  }
}

$: if ($portfolio?.coins) {
  const params = {
    logging,
    symbols: Object.values($portfolio.symbols),
  };
  streamer(params);
}

onDestroy(async () => {
  if (reader) await reader.cancel("client destroyed");
  controller.abort();
  console.log("finished");
});

问题根因

Firebase Hosting默认代理配置不支持**分块传输编码(Chunked Transfer Encoding)**的流式响应,它会尝试缓冲整个响应后再返回给客户端。而你的API返回的是ReadableStream,属于实时分块传输,Firebase代理无法处理该场景,最终导致超时(504)或代理错误(502)。

解决步骤

1. 修改Firebase Rewrite配置,开启流式支持

在run节点添加配置,强制代理透传流式响应:

{
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "run": {
          "serviceId": "vc-ticker-app",
          "region": "us-central1",
          "serveHostingAssets": false,
          "httpHeaders": {
            "Transfer-Encoding": "chunked",
            "Cache-Control": "no-cache"
          }
        }
      }
    ]
  }
}
  • serveHostingAssets: false:关闭Firebase Hosting的静态资源缓冲逻辑,让所有请求直接转发给Cloud Run
  • Transfer-Encoding: chunked:告知代理保留分块传输格式
  • Cache-Control: no-cache:禁止代理缓冲响应内容

2. 调整Cloud Run API的响应头

在SvelteKit的API路由(如+server.js)中,确保返回响应时携带以下头信息:

export async function POST({ request }) {
  // 你的流式处理逻辑
  const stream = ...; // 生成ReadableStream的代码

  return new Response(stream, {
    headers: {
      'Content-Type': 'application/json',
      'Transfer-Encoding': 'chunked',
      'Connection': 'keep-alive',
      'Cache-Control': 'no-cache, no-store'
    }
  });
}

这些头信息会明确告知代理不要修改响应内容,直接将分块数据实时转发给客户端。

3. 检查Cloud Run超时设置

Cloud Run默认请求超时为5分钟,如果你的流式连接需要更长时间,可在Cloud Run控制台将服务超时时间调至最大值(60分钟)。

4. 验证请求路径

确认客户端fetch的路径无误,使用相对路径时需确保页面base路径正确,避免请求发错地址。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 00:35:17