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

FCM令牌存储与刷新更新优化方案——事件群聊推送场景

最优实现方案

核心问题分析

当前代码的性能瓶颈在于:

  • 循环逐个查询用户文档获取FCM令牌,产生大量Firestore单文档请求,耗时累积
  • 调用FCM单播接口逐个发送通知,而非使用效率更高的多播接口

方案一:批量查询令牌 + FCM多播推送

无需修改现有存储结构,通过批量读取用户令牌+FCM多播接口大幅提升推送效率,同时避免令牌刷新的维护成本。

具体实现步骤

  1. 从活动文档中获取所有参与者ID,排除消息发送者
  2. 批量查询这些用户的文档,一次性提取所有有效FCM令牌
  3. 使用FCM的sendMulticast接口批量发送通知

修改后的代码示例

exports.sendNotification = functions.firestore
  .document("event/{eventid}/{chat}/{chatid}")
  .onCreate(async (snap, context) => {
    await processData(snap, context);
    return null;
  });

async function processData(snap, context) {
  const doc = snap.data();
  const eventID = context.params.eventid;
  const senderID = doc.sender;
  const senderName = doc.senderName;

  // 获取活动信息和参与者列表
  const eventDoc = await admin.firestore().collection("event").doc(eventID).get();
  const eventData = eventDoc.data();
  const eventName = eventData.eventName;
  const joinedBy = eventData.joinedBy.filter(uid => uid !== senderID);

  if (joinedBy.length === 0) {
    console.log("无需要通知的用户");
    return;
  }

  // 批量获取用户文档(Firestore getAll支持最多500个文档)
  const userDocs = await admin.firestore().getAll(
    ...joinedBy.map(uid => admin.firestore().collection("people").doc(uid))
  );

  // 提取有效FCM令牌
  const tokens = userDocs
    .filter(doc => doc.exists && doc.data().pushToken)
    .map(doc => doc.data().pushToken);

  if (tokens.length === 0) {
    console.log("无有效FCM令牌");
    return;
  }

  // 构建通知 payload
  const payload = {
    notification: {
      title: `${eventName} : ${senderName}`,
      body: doc.text,
      badge: "1",
      sound: "default"
    },
  };

  // 批量发送通知
  const response = await admin.messaging().sendMulticast({
    tokens: tokens,
    notification: payload.notification
  });

  // 处理发送结果,记录失败的令牌
  console.log(`成功发送 ${response.successCount} 条通知,失败 ${response.failureCount} 条`);
  response.responses.forEach((resp, idx) => {
    if (!resp.success) {
      console.error(`令牌 ${tokens[idx]} 发送失败: ${resp.error}`);
    }
  });
}

方案二:引入订阅中间集合(可选,应对超大规模场景)

如果未来用户量极大,批量查询仍存在性能压力,可以新增一个event_subscriptions集合,每个文档代表用户对活动的订阅,结构如下:

event_subscriptions/{subscriptionId}
- userId: string // 用户UID
- eventId: string // 活动ID
- fcmToken: string // FCM令牌

优势

  • 发送通知时,直接查询event_subscriptions中eventId匹配的文档,一次性获取所有令牌,无需关联用户集合
  • 令牌刷新时,通过批量更新该用户的所有订阅文档(使用where("userId", "==", uid)查询后批量更新)

注意事项

  • 需在用户加入/退出活动时,同步维护event_subscriptions的文档
  • 令牌刷新时,使用Firestore批量更新操作(最多500个文档/批次),避免多次请求

关键优化点总结

  • 减少Firestore请求次数:用批量查询替代循环单文档查询
  • 提升FCM发送效率:用多播接口替代单播接口
  • 避免冗余维护:优先选择无需修改核心存储结构的方案,降低令牌刷新的维护成本

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 21:48:27