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

FCM推送失败重试流程优化咨询(Node.js+Drizzle)

问题描述

我基于Node.js后端,用Drizzle和Firebase Cloud Messaging(FCM)实现推送通知功能,支持用户绑定多台设备。当前通知流程如下:

  1. 目标为向一组userId发送通知;
  2. 查询数据库获取这些用户的所有FCM令牌:
.where(inArray(userDevices.userId, usersIds));
  1. 向Firebase发送批量推送;
  2. 若Firebase针对特定令牌返回可重试错误(如网络问题、内部服务器错误),将失败令牌映射回对应的userId;
  3. 使用pg-boss调度重试任务,传入失败的userId数组。

现在遇到的问题是重复通知:比如用户A有两台设备(令牌1和令牌2),令牌1推送失败而令牌2成功,系统会调度用户A的重试任务。重试时会重新查询用户A的所有令牌并推送,导致令牌2收到重复通知。

如果直接用失败令牌调度重试,又担心期间用户登出导致令牌失效,仍需查询数据库验证令牌有效性。

请问:将失败令牌传入后台任务是否是处理FCM重试的最佳实践?应如何修改现有流程?


解决方案

关于「失败令牌传入后台任务是否为最佳实践」

是的,针对FCM可重试错误,直接传递失败令牌+对应通知内容是更精准的重试方案,能从根源避免重复通知问题。但需要结合令牌有效性校验,平衡准确性和效率。

修改现有流程的具体步骤

  • 批量推送后拆分结果,记录失败令牌明细
    调用FCM批量接口后,遍历每个令牌的返回结果:

    • 成功的令牌直接标记完成,无需后续处理;
    • 返回可重试错误(如UNAVAILABLE、INTERNAL)的令牌,记录令牌本身、对应userId、本次通知内容(或唯一标识);
    • 返回不可重试错误(如INVALID_REGISTRATION、NOT_REGISTERED)的令牌,直接从数据库删除,避免无效重试。
  • 调度重试任务时传入失败令牌列表
    使用pg-boss调度任务时,将失败令牌数组、通知内容作为任务参数,而非userId。这样重试时无需重新查询用户所有令牌,只针对失败的令牌推送。

  • 重试前先校验令牌有效性
    执行重试任务时,先做两步校验:

    1. 查询数据库,确认该令牌仍属于对应userId(未被登出删除);
    2. 可选:调用FCM的validate-only接口快速校验令牌有效性,避免浪费推送请求。
      仅对校验通过的令牌发起推送。
  • 设置合理的重试策略
    结合pg-boss的重试机制,配置指数退避策略(比如第一次重试间隔1分钟,第二次5分钟,最多重试3次),避免短时间内重复请求FCM服务器。

  • 定期清理无效令牌
    定期通过FCM批量校验接口检测数据库中令牌的有效性,删除失效令牌,减少重试时的校验成本。

重试任务逻辑示例代码

async function retryFcmPush(failedTokens, notificationContent) {
  // 1. 从数据库校验令牌有效性
  const validTokens = await db.select()
    .from(userDevices)
    .where(inArray(userDevices.token, failedTokens));

  // 2. 仅对有效令牌发起推送
  if (validTokens.length > 0) {
    const tokensToPush = validTokens.map(item => item.token);
    await fcm.sendMulticast({
      tokens: tokensToPush,
      notification: notificationContent
    });
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 13:04:53