Firebase Cloud Messaging重复推送通知直至用户打开问题排查
问题根因
你遇到的重复推送问题是两个代码缺陷共同导致的:
- HTTP类型的Cloud Functions要求所有执行分支必须显式调用
response对象的方法返回结果、终止请求,否则平台会判定本次函数执行超时/失败,按照默认规则周期性重试——你当前代码所有分支都是return null/return error,从未给平台返回执行完成的响应,默认超时时间刚好是60秒,和你观察到的“每分钟重复收到通知”的间隔完全吻合。 - 推送逻辑没有做幂等校验:重试触发时只要数据库里的消息还保持
read: false状态,就会再次执行推送,直到用户点开聊天把消息标记为已读,判断条件不成立才会停止推送,和你描述的“打开通知后推送停止”的现象完全匹配。
排查步骤
- 拉取函数执行日志,对比重复触发的请求的执行上下文,可看到重复请求是平台自动发起的重试请求,而非业务侧重复调用接口
- 检查函数所有逻辑分支,确认是否存在未调用
response.send()/response.end()的路径 - 验证推送逻辑是否存在重复执行的可能,即同一条消息在多次函数调用中是否会重复触发推送
- 检查函数配置页的重试开关,确认是否误开启了失败自动重试配置
修复方案
核心修复两点:一是所有分支必须显式返回HTTP响应,终止函数执行;二是增加幂等标记,避免同一条消息被重复推送。
修复后的参考代码:
export const sendNotification = functions.https.onRequest((request, response) => { return cors()(request, response, async () => { const { senderID, receiverID, messageSent } = request.body; const db = admin.database(); try { const ref = db.refFromURL(`https://firebase-url/recentchats/${receiverID}/${senderID}`) const data = await ref.once("value"); const chatRecord = data.val(); functions.logger.warn("DATA : ", chatRecord); // 幂等拦截:已经发过通知的消息直接返回,不重复推送 if (chatRecord.notificationPushed) { response.status(200).send({ code: 0, msg: "notification already sent" }); return; } if (messageSent === chatRecord.message && !chatRecord.read) { const senderName = chatRecord.sender; const notification: MessagingPayload = { notification: { title: `Nouveau message de ${senderName}`, body: messageSent, }, data: { route: `/app/tabs/chat/${senderID}/${senderName}` } }; functions.logger.warn(`${senderName} a envoyé le message : ${messageSent}`); await admin.messaging().sendToTopic(receiverID, notification); // 标记该条消息已完成推送,阻断重试时的重复发送逻辑 await ref.update({ notificationPushed: true }); // 显式返回成功响应,告知平台函数执行完成 response.status(200).send({ code: 0, msg: "send success" }); return; } else { // 不满足推送条件的分支也要正常返回响应 response.status(200).send({ code: 0, msg: "push condition not met" }); return; } } catch (error) { functions.logger.error("ERROR : ", error); // 异常分支也要返回响应,避免函数挂起触发重试 response.status(500).send({ code: 500, error: error }); return; } }); });
额外优化建议
- 业务侧调用该接口时建议传入唯一的消息ID
messageID,幂等校验基于唯一消息ID实现,比对比消息内容更可靠,避免内容相同导致的判断误判 - 可根据业务需求调整云函数超时时间,不要使用默认的60秒过长超时配置
- 若不需要重试逻辑,可在云函数配置页关闭“失败重试”开关作为兜底防护
内容的提问来源于stack exchange,提问作者elition
相关产品推荐
相关产品推荐

