FCM推送失败重试流程优化咨询(Node.js+Drizzle)
问题描述
我基于Node.js后端,用Drizzle和Firebase Cloud Messaging(FCM)实现推送通知功能,支持用户绑定多台设备。当前通知流程如下:
- 目标为向一组userId发送通知;
- 查询数据库获取这些用户的所有FCM令牌:
.where(inArray(userDevices.userId, usersIds));
- 向Firebase发送批量推送;
- 若Firebase针对特定令牌返回可重试错误(如网络问题、内部服务器错误),将失败令牌映射回对应的userId;
- 使用pg-boss调度重试任务,传入失败的userId数组。
现在遇到的问题是重复通知:比如用户A有两台设备(令牌1和令牌2),令牌1推送失败而令牌2成功,系统会调度用户A的重试任务。重试时会重新查询用户A的所有令牌并推送,导致令牌2收到重复通知。
如果直接用失败令牌调度重试,又担心期间用户登出导致令牌失效,仍需查询数据库验证令牌有效性。
请问:将失败令牌传入后台任务是否是处理FCM重试的最佳实践?应如何修改现有流程?
解决方案
关于「失败令牌传入后台任务是否为最佳实践」
是的,针对FCM可重试错误,直接传递失败令牌+对应通知内容是更精准的重试方案,能从根源避免重复通知问题。但需要结合令牌有效性校验,平衡准确性和效率。
修改现有流程的具体步骤
批量推送后拆分结果,记录失败令牌明细
调用FCM批量接口后,遍历每个令牌的返回结果:- 成功的令牌直接标记完成,无需后续处理;
- 返回可重试错误(如
UNAVAILABLE、INTERNAL)的令牌,记录令牌本身、对应userId、本次通知内容(或唯一标识); - 返回不可重试错误(如
INVALID_REGISTRATION、NOT_REGISTERED)的令牌,直接从数据库删除,避免无效重试。
调度重试任务时传入失败令牌列表
使用pg-boss调度任务时,将失败令牌数组、通知内容作为任务参数,而非userId。这样重试时无需重新查询用户所有令牌,只针对失败的令牌推送。重试前先校验令牌有效性
执行重试任务时,先做两步校验:- 查询数据库,确认该令牌仍属于对应userId(未被登出删除);
- 可选:调用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
相关产品推荐
相关产品推荐

