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

FCM单条消息仅支持1000个注册ID,PHP如何推送至500000台设备?

如何在PHP中向50万台设备推送FCM通知?

兄弟,这个场景我太熟了!FCM明确规定单条消息最多包含1000个设备注册ID,要搞定50万的推送量,核心思路就是分批处理,再配合一些优化技巧保证推送效率和成功率。下面给你一步步拆解:

1. 第一步:拆分设备ID为1000个一组的批次

首先得把50万的token数组拆分成每1000个一组的子数组,PHP自带的array_chunk()函数完美适配这个需求,一行代码就能搞定:

// 假设你从数据库或其他存储拿到了50万个设备token
$allDeviceTokens = [...]; // 你的50万token集合

// 拆分每1000个token为一个批次
$tokenBatches = array_chunk($allDeviceTokens, 1000);
// 拆分后会得到500个批次(500000 / 1000 = 500)

2. 批量发送FCM请求

接下来要给每个批次发送FCM请求,这里有几个关键要点要注意:

基础同步发送示例(原生PHP + Guzzle)

如果是小批量测试或者没有框架支持,用Guzzle发送同步请求是最直接的方式,同时要处理响应里的错误信息:

use GuzzleHttp\Client;

// 初始化Guzzle客户端
$client = new Client();
// 替换成你的FCM服务器密钥
$fcmServerKey = 'YOUR_FCM_SERVER_KEY';
$fcmApiEndpoint = 'https://fcm.googleapis.com/fcm/send';

// 遍历每个批次发送
foreach ($tokenBatches as $batchIndex => $batch) {
    // 构造FCM请求 payload
    $payload = [
        'registration_ids' => $batch,
        'notification' => [
            'title' => '你的通知标题',
            'body' => '你的通知内容'
        ],
        // 可选:添加自定义数据字段
        'data' => [
            'custom_key' => 'custom_value'
        ]
    ];

    try {
        $response = $client->post($fcmApiEndpoint, [
            'headers' => [
                'Authorization' => 'key=' . $fcmServerKey,
                'Content-Type' => 'application/json'
            ],
            'json' => $payload
        ]);

        // 解析响应,处理失败的token
        $responseData = json_decode($response->getBody(), true);
        if (isset($responseData['results'])) {
            foreach ($responseData['results'] as $tokenIndex => $result) {
                if (isset($result['error'])) {
                    $invalidToken = $batch[$tokenIndex];
                    // 这里要把无效token从你的存储中移除(比如数据库)
                    // 常见错误:invalid_registration、not_registered都是无效token
                    error_log("批次{$batchIndex}中无效token: {$invalidToken},错误原因:{$result['error']}");
                }
            }
        }
    } catch (\Exception $e) {
        // 捕获请求异常,比如网络错误、限流等,可添加重试逻辑
        error_log("批次{$batchIndex}发送失败:{$e->getMessage()}");
    }

    // 可选:添加小延迟,避免触发FCM频率限制
    usleep(100000); // 暂停0.1秒
}

进阶:用队列异步处理(推荐)

如果是在Laravel、Symfony这类框架中,强烈建议把每个批次的推送任务放到队列中处理,这样不会阻塞前端请求,也能避免长时间运行导致的超时问题。

以Laravel为例:

  1. 创建一个队列任务:php artisan make:job SendFcmBatch
  2. 在任务类中编写推送逻辑:
namespace App\Jobs;

use GuzzleHttp\Client;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class SendFcmBatch implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $tokenBatch;

    public function __construct(array $tokenBatch)
    {
        $this->tokenBatch = $tokenBatch;
    }

    public function handle()
    {
        $client = new Client();
        $fcmServerKey = config('services.fcm.server_key');
        $payload = [
            'registration_ids' => $this->tokenBatch,
            'notification' => [
                'title' => '你的通知标题',
                'body' => '你的通知内容'
            ]
        ];

        try {
            $response = $client->post('https://fcm.googleapis.com/fcm/send', [
                'headers' => [
                    'Authorization' => 'key=' . $fcmServerKey,
                    'Content-Type' => 'application/json'
                ],
                'json' => $payload
            ]);

            // 同样处理无效token逻辑...
        } catch (\Exception $e) {
            $this->fail($e); // 标记任务失败,方便重试
        }
    }
}
  1. 分发任务:
// 拆分批次后,循环分发任务
foreach ($tokenBatches as $batch) {
    SendFcmBatch::dispatch($batch);
}

3. 关键优化技巧

  • 及时清理无效token:FCM返回的invalid_registration、not_registered错误对应的token是无效的,一定要从你的存储中移除,避免后续重复浪费请求。
  • 控制请求频率:FCM有隐性的频率限制,不要一次性发送所有批次,加个小延迟(比如0.1秒)或者控制队列并发数,避免被限流。
  • 使用FCM主题订阅(可选):如果你的设备可以按主题分组(比如同一类用户、同一业务场景),可以让设备订阅主题,直接给主题发消息,FCM会自动推送给所有订阅设备,省去自己拆分token的麻烦。
  • 监控与日志:一定要记录每个批次的发送结果,成功数、失败数、错误类型,方便后续排查问题。

4. 百万级以上的扩展方案

如果后续设备量增长到百万级,可以考虑:

  • 多进程并发发送:用PHP的pcntl_fork()创建子进程,每个进程处理部分批次(注意进程管理,避免资源耗尽)。
  • 使用FCM v1 API:相比传统的服务器密钥认证,v1 API用OAuth2认证,权限更精细,适合大规模推送场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:34:26