Firebase函数storeCategories模拟器正常生产环境报socket类错误如何解决
问题原因分析
- 全局异步调用无等待:你在云函数全局作用域直接调用了
storeCategories()和storeTrending()两个异步方法,Firebase云函数的全局代码执行阶段不会等待异步任务完成,实例会在逻辑跑完前被强制回收。storeTrending()仅需1次接口请求,大概率能在实例回收前执行完成所以无报错,而storeCategories()需要批量请求十余个TMDB接口,耗时更长,就会出现socket提前断开的报错。 - 并发请求超限:你用
Promise.all一次性发起所有分类的并发请求,要么触发了TMDB的API速率限制,要么超出了云函数的出站连接阈值,也会导致连接被强制断开。 - 异步操作未等待:
storeTrending()中的Firestore写入操作没有加await,实际也存在写入失败的概率,只是未抛出明显错误。
修复方案
1. 不要在全局作用域执行异步逻辑
按照你规划的调整为定时调度云函数,确保所有异步逻辑都在云函数的处理回调内执行,云函数会等待回调内的异步任务完成再回收实例。
2. 限制并发请求数量
引入限流工具控制同时发起的请求数,避免触发接口或平台的连接限制。
3. 所有异步操作加await
确保IO类操作执行完成再结束逻辑。
修复后示例代码
/** @format */ const functions = require("firebase-functions"); const cors = require("cors"); const express = require("express"); const fetch = require("node-fetch"); // 引入p-limit做并发限制,需要提前安装:npm install p-limit@3 (v3版本兼容CommonJS) const pLimit = require('p-limit'); const limit = pLimit(3); // 同时最多发起3个请求 const apiKey = functions.config().tmdbapi.key; const admin = require("firebase-admin"); admin.initializeApp(); const db = admin.firestore(); var app = express(); app.use(cors()); const storeCategories = async () => { try { // GET CATEGORY IDS const categoriesIDRes = await fetch( `https://api.themoviedb.org/3/genre/movie/list?api_key=${apiKey}`, { timeout: 10000 } // 加超时配置避免长时间挂起 ); const categoriesInfo = await categoriesIDRes.json(); // CREATE CATEGORY API LINKS const categoriesURLs = categoriesInfo.genres.map( (el) => `https://api.themoviedb.org/3/discover/movie?api_key=${apiKey}&language=en-US&sort_by=popularity.desc&include_adult=true&include_video=true&page=1&with_genres=${el.id}&with_watch_monetization_types=flatrate` ); // 限流请求 const categoriesRes = await Promise.all( categoriesURLs.map((el) => limit(() => fetch(el, { timeout: 10000 }))) ); const data = await Promise.all( categoriesRes.map((el) => el.json()) ); const batch = db.batch(); // WRITE TO FIRESTORE FOR FASTER INITIAL LOAD data.forEach((category, idx) => { const ref = db .collection("categories") .doc(categoriesInfo.genres[idx].name); batch.set(ref, category); }); const categoriesInfoRef = db.collection("categoryIDs").doc("IDs"); batch.set(categoriesInfoRef, categoriesInfo); return batch.commit(); } catch (err) { console.log(err); throw err; // 抛出错误方便云函数日志记录 } }; const storeTrending = async () => { try { const res = await fetch( `https://api.themoviedb.org/3/trending/all/week?api_key=${apiKey}`, { timeout: 10000 } ); const data = await res.json(); // 加await等待写入完成 await db.collection("categories").doc("trending").set(data); } catch (err) { console.log(err); throw err; } }; // 定时调度云函数示例,每天0点执行 exports.scheduledSyncData = functions.pubsub.schedule('0 0 * * *') .timeZone('Asia/Shanghai') // 按需调整时区 .onRun(async (context) => { await Promise.all([ storeCategories(), storeTrending() ]); return null; });
内容的提问来源于stack exchange,提问作者mattavero
相关产品推荐
相关产品推荐

