FCM令牌存储与刷新更新优化方案——事件群聊推送场景
最优实现方案
核心问题分析
当前代码的性能瓶颈在于:
- 循环逐个查询用户文档获取FCM令牌,产生大量Firestore单文档请求,耗时累积
- 调用FCM单播接口逐个发送通知,而非使用效率更高的多播接口
方案一:批量查询令牌 + FCM多播推送
无需修改现有存储结构,通过批量读取用户令牌+FCM多播接口大幅提升推送效率,同时避免令牌刷新的维护成本。
具体实现步骤
- 从活动文档中获取所有参与者ID,排除消息发送者
- 批量查询这些用户的文档,一次性提取所有有效FCM令牌
- 使用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
相关产品推荐
相关产品推荐

