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

基于Node.js+Express的FCM批量推送全用户通知方案咨询

实现大规模FCM推送的完整方案(结合Kue队列)

我之前也碰到过类似的大规模FCM推送场景,单次1000个令牌的限制确实是个坎儿,结合Kue来做异步批量处理是非常合适的方案,下面给你一套完整的实现思路和代码示例,亲测好用:

核心思路:令牌分批 + 异步队列

FCM的sendMulticast接口单次最多支持1000个设备令牌,所以核心逻辑就是:

  1. 从数据库分页查询所有有效的FCM令牌
  2. 每1000个令牌分为一批,将每批的推送任务丢进Kue队列
  3. 由队列异步处理每一批推送,同时处理失败重试、限流等问题

具体实现步骤(基于Node.js)

1. 初始化依赖和客户端

首先确保你已经安装了kue、firebase-admin和对应的数据库驱动,然后初始化队列和FCM客户端:

const kue = require('kue');
const { initializeApp } = require('firebase-admin/app');
const { getMessaging } = require('firebase-admin/messaging');

// 初始化Firebase Admin(替换成你的服务账号密钥)
const firebaseApp = initializeApp({
  credential: require('./serviceAccountKey.json')
});
const messaging = getMessaging(firebaseApp);

// 初始化Kue队列(依赖Redis,确保本地或服务器上的Redis已启动)
const queue = kue.createQueue();

2. 编写队列任务处理器

定义一个处理单批令牌推送的任务函数,包含失败重试、日志记录等关键逻辑:

// 处理FCM推送任务,并发数设为5(可根据FCM配额调整)
queue.process('fcm-push', 5, async (job, done) => {
  const { tokens, notification } = job.data;
  
  try {
    // 调用FCM批量推送接口
    const response = await messaging.sendMulticast({
      tokens: tokens,
      notification: notification
    });

    // 收集推送失败的令牌,后续重试
    const failedTokens = [];
    response.responses.forEach((resp, index) => {
      if (!resp.success) {
        failedTokens.push(tokens[index]);
        console.error(`推送失败至令牌 ${tokens[index]}: ${resp.error.message}`);
      }
    });

    // 对失败令牌进行重试(最多3次)
    if (failedTokens.length > 0 && job.attempts < 3) {
      queue.create('fcm-push', {
        tokens: failedTokens,
        notification: notification
      }).attempts(3).save(err => {
        if (err) console.error('失败令牌重新入队失败:', err);
      });
    }

    done();
  } catch (error) {
    console.error('推送任务执行失败:', error);
    // 标记任务失败,Kue会自动触发重试
    done(error);
  }
});

3. 分批获取令牌并加入队列

写一个函数,从数据库分页拉取令牌,每批1000个,然后创建队列任务:

async function enqueueFullPush(notification) {
  const batchSize = 1000;
  let offset = 0;

  while (true) {
    // 替换成你的数据库查询逻辑,分页获取有效FCM令牌
    const userRecords = await yourDatabase.query(`
      SELECT fcm_token FROM users 
      WHERE fcm_token IS NOT NULL AND fcm_token != ''
      LIMIT ${batchSize} OFFSET ${offset}
    `);

    const tokens = userRecords.map(record => record.fcm_token);
    if (tokens.length === 0) break;

    // 将当前批次加入队列
    queue.create('fcm-push', {
      tokens: tokens,
      notification: notification
    }).attempts(3).save(err => {
      if (err) console.error(`批次 ${offset/batchSize + 1} 入队失败:`, err);
    });

    offset += batchSize;
    console.log(`已加入批次 ${offset/batchSize},共 ${tokens.length} 个令牌`);
  }
}

4. 管理员触发推送的API接口

在你的管理后台API中添加一个接口,让管理员发起全量推送:

// 假设使用Express框架
const express = require('express');
const app = express();
app.use(express.json());

app.post('/api/admin/broadcast-push', async (req, res) => {
  const { title, body } = req.body;
  
  if (!title || !body) {
    return res.status(400).json({ error: '推送标题和内容不能为空' });
  }

  try {
    await enqueueFullPush({
      title: title,
      body: body
    });
    res.json({ message: '全量推送任务已加入队列,将异步处理' });
  } catch (error) {
    console.error('触发全量推送失败:', error);
    res.status(500).json({ error: '服务器内部错误,推送任务未触发' });
  }
});

app.listen(3001, () => console.log('管理后台API启动'));

关键优化建议

  • 令牌清理:定期清理数据库中无效的FCM令牌(比如推送返回InvalidRegistration的令牌),减少无效请求
  • 监控与日志:启动Kue的UI面板(kue.app.listen(3002)),可以直观查看任务的成功/失败/等待状态;同时将推送日志持久化到数据库,方便后续排查问题
  • 限流控制:根据你的FCM配额调整队列的并发数,避免触发FCM的请求限流(FCM对不同等级的项目有不同的每分钟请求限额)
  • 重试策略:对于多次重试仍失败的令牌,建议单独存储,后续人工排查原因(比如用户卸载应用、令牌过期等)

备选方案

如果Kue不符合你的技术栈或需求,也可以考虑用Bull(功能更丰富的Node.js队列库),或者自己基于数据库实现简单的任务队列,但成熟的队列库能帮你省掉很多任务持久化、重试、并发控制的麻烦。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:21:04