Firebase定时函数中如何适配第三方API每分钟5次的调用速率限制
Firebase定时函数中如何适配第三方API每分钟5次的调用速率限制
看起来你现在遇到的核心问题是第三方API限流导致的调用失败,主要有两个根源:一是原代码用了forEach(async doc => { ... })遍历文档,但forEach不支持异步等待,会一次性触发所有API请求,直接撞上限流;二是完全没对API调用做速率控制。下面我来帮你重构代码解决这个问题:
一、先修复异步遍历的无效问题
原代码里fQuerySnapshot.forEach(async(doc) => { ... })的写法是错的——forEach不会等待回调里的异步操作完成,会瞬间把所有API请求发出去,这是触发限流的直接原因。我们需要改用for...of循环来遍历,确保异步操作能被顺序等待。
二、实现速率控制逻辑
API限制每分钟最多5次调用,我们可以维护一个全局计数器,每完成5次API调用后,就等待60秒再继续下一批。注意:每一次findStockPrice调用都要计入计数器,不管它来自哪个用户或哪只股票。
三、重构后的完整代码
exports.dailyNetworth = onSchedule("every 24 hours", async (event: any) => { // 限流配置:每分钟最多5次API调用 const MAX_CALLS_PER_MINUTE = 5; let apiCallCount = 0; // 遍历所有有效用户,用for...of确保异步等待(如果validUsers是Map,keys()返回迭代器) for (const uid of validUsers.keys()) { const finColRef = db.collection(`users/${uid}/finance`); const fQuerySnapshot = await finColRef.orderBy("Date", 'desc').limit(1).get(); // 替换forEach为for...of,确保异步操作能被等待 for (const doc of fQuerySnapshot.docs) { const data = doc.data(); const finance = new Finance(); finance.assets = data.assets; const astClass = 'Stocks'; // 修复原代码中astClass未定义的问题,对应你的股票资产类 // 遍历用户的每只股票 for (const ast of Object.keys(finance.assets[astClass])) { const asset: Stock = finance.assets[astClass][ast]; // 速率控制检查:每5次调用后等待60秒 apiCallCount++; if (apiCallCount >= MAX_CALLS_PER_MINUTE) { console.log(`已达API调用上限,等待60秒后继续...`); // 用Promise封装setTimeout,适配async/await逻辑 await new Promise(resolve => setTimeout(resolve, 60000)); apiCallCount = 0; // 重置计数器 } // 调用第三方API获取股票价格 const stkPrice: any = await findStockPrice(asset.Name, asset["Share Count"]); if (stkPrice !== null) { // 更新资产数据 finance.assets[astClass][ast].value = stkPrice; finance.assets[astClass][ast].lastUpdateDate = Timestamp.now(); } } // 重要:将更新后的资产数据写回Firestore(原代码漏掉了这一步,否则修改不会生效) await doc.ref.update({ assets: finance.assets }); } } }); // 第三方API调用函数(保留你的原有逻辑) async function findStockPrice(symbol: string, quantity: number) { let value = null; try { const url = `https://someurl.com?${symbol}`; const header = { // 你的请求头配置 }; const response = await fetch(url, { headers: header }); const json = await response.json(); const rate = json["rate"]; value = quantity * rate; } catch (error: any) { logger.error(error); } return value; }
额外注意事项
- 函数超时问题:如果你的用户数量较多(比如总API调用次数超过 5*9=45次,对应总执行时间超过9分钟),Firebase Functions的默认最大超时时间是540秒(9分钟),此时会导致函数中途终止。这种情况下建议用Cloud Tasks把任务拆成多个小任务,每个任务处理少量用户/股票,避免超时。
- 容错处理:可以在API调用失败时加重试逻辑(比如指数退避),避免单次失败导致整个用户的股票更新中断。
- 计数器调整:如果某次API调用失败,当前逻辑依然会计数,你可以根据需求调整(比如失败时不计数)。
备注:内容来源于stack exchange,提问作者Moblize IT
相关产品推荐
相关产品推荐

