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

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;
}

额外注意事项

  1. 函数超时问题:如果你的用户数量较多(比如总API调用次数超过 5*9=45次,对应总执行时间超过9分钟),Firebase Functions的默认最大超时时间是540秒(9分钟),此时会导致函数中途终止。这种情况下建议用Cloud Tasks把任务拆成多个小任务,每个任务处理少量用户/股票,避免超时。
  2. 容错处理:可以在API调用失败时加重试逻辑(比如指数退避),避免单次失败导致整个用户的股票更新中断。
  3. 计数器调整:如果某次API调用失败,当前逻辑依然会计数,你可以根据需求调整(比如失败时不计数)。

备注:内容来源于stack exchange,提问作者Moblize IT

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 17:03:02