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

Firebase Cloud Messaging重复推送通知直至用户打开问题排查

问题根因

你遇到的重复推送问题是两个代码缺陷共同导致的:

  1. HTTP类型的Cloud Functions要求所有执行分支必须显式调用response对象的方法返回结果、终止请求,否则平台会判定本次函数执行超时/失败,按照默认规则周期性重试——你当前代码所有分支都是return null/return error,从未给平台返回执行完成的响应,默认超时时间刚好是60秒,和你观察到的“每分钟重复收到通知”的间隔完全吻合。
  2. 推送逻辑没有做幂等校验:重试触发时只要数据库里的消息还保持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;
    }
  });
});

额外优化建议

  • 业务侧调用该接口时建议传入唯一的消息IDmessageID,幂等校验基于唯一消息ID实现,比对比消息内容更可靠,避免内容相同导致的判断误判
  • 可根据业务需求调整云函数超时时间,不要使用默认的60秒过长超时配置
  • 若不需要重试逻辑,可在云函数配置页关闭“失败重试”开关作为兜底防护

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 13:33:16