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

如何用PHP高效向20万用户同步发送Firebase推送通知

解决FCM批量推送延迟问题的方案

当前用户基数20万,直播活动正在进行,需要通过PHP向所有用户同步发送FCM推送通知,但FCM单次请求最多支持1000个设备ID,导致最后一批用户在直播开始10分钟后才收到通知。以下是可行的解决措施:

核心优化方案

1. 改用FCM主题推送(最优解)

让所有用户订阅同一个直播相关主题(比如live_event_xxx),直接向该主题发送推送。FCM会自动负责将消息分发给所有订阅用户,无需手动分批处理,完全避开单次1000个设备的限制,且分发效率由FCM后台保障,能确保所有用户几乎同时收到通知。

2. 并行处理批量请求

如果必须使用设备ID列表推送,不要串行逐个发送每批请求,改成并行发送多批请求:

  • 使用curl_multi扩展同时发起多个HTTP请求,一次性处理多批(比如10-20批)1000个设备的推送,大幅压缩总耗时。
  • 或者借助异步任务队列(如Redis队列+PHP Worker进程),将200批推送任务放入队列,由多个Worker同时处理,避免单进程阻塞等待。

3. 修正原代码的批量推送逻辑

你的现有代码存在一个关键问题:当$regIds是数组时,没有设置FCM要求的registration_ids字段,导致无法正确批量推送。先修正这个基础问题:

function sendMsg($regIds, $data, $device_type=''){
    $apiKey = $this->data['api_key']; // 修正apiKey获取方式,原代码未正确使用
    $fields = array(
        'priority' => 'high',
        'data' => array(
            'title' => $this->data['title'],
            "body" => $this->data['message'],
            'sound' => 'default',
            'message' => $this->data['message'],
            'screen' => $this->data['screen'],
            'screen_id' => $this->data['screen_id']
        )
    );
    if($this->data['screen_data']) {
        $fields['data']['screen_data'] = $this->data['screen_data'];
    }
    if($this->data['notification_id']) {
        $fields['data']['notification_id'] = $this->data['notification_id'];
    }

    // 修正批量推送逻辑
    if(is_array($regIds)) {
        // 确保每批不超过1000个设备ID
        if(count($regIds) > 1000) {
            throw new Exception("每批设备ID数量不能超过1000");
        }
        $fields['registration_ids'] = $regIds;
    } else {
        $fields['to'] = $regIds;
    }

    if($device_type != 'android') {
        $fields['notification'] = array(
            'title' => $this->data['title'],
            'body' => $this->data['message'],
            'sound' => 'default'
        );
    }

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, 'https://fcm.googleapis.com/fcm/send');
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: key='.$apiKey, 'Content-Type: application/json'));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); // 建议开启SSL验证,提升安全性
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));
    $result = curl_exec($ch);
    curl_close($ch);
    return $result;
}

4. 并行请求示例(使用curl_multi)

以下是一个简单的并行处理多批设备ID的示例,可大幅减少总推送时间:

function sendBulkMsg($allRegIds, $data, $device_type=''){
    $batchSize = 1000;
    $batches = array_chunk($allRegIds, $batchSize);
    $mh = curl_multi_init();
    $handles = [];

    foreach($batches as $batch) {
        $ch = curl_init();
        $apiKey = $data['api_key'];
        $fields = array(
            'priority' => 'high',
            'registration_ids' => $batch,
            'data' => array(
                'title' => $data['title'],
                "body" => $data['message'],
                'sound' => 'default',
                'message' => $data['message'],
                'screen' => $data['screen'],
                'screen_id' => $data['screen_id']
            )
        );
        if($data['screen_data']) {
            $fields['data']['screen_data'] = $data['screen_data'];
        }
        if($data['notification_id']) {
            $fields['data']['notification_id'] = $data['notification_id'];
        }
        if($device_type != 'android') {
            $fields['notification'] = array(
                'title' => $data['title'],
                'body' => $data['message'],
                'sound' => 'default'
            );
        }

        curl_setopt($ch, CURLOPT_URL, 'https://fcm.googleapis.com/fcm/send');
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: key='.$apiKey, 'Content-Type: application/json'));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));
        curl_multi_add_handle($mh, $ch);
        $handles[] = $ch;
    }

    // 执行所有请求
    $running = null;
    do {
        curl_multi_exec($mh, $running);
        curl_multi_select($mh);
    } while($running > 0);

    // 获取结果并关闭句柄
    $results = [];
    foreach($handles as $ch) {
        $results[] = curl_multi_getcontent($ch);
        curl_multi_remove_handle($mh, $ch);
        curl_close($ch);
    }
    curl_multi_close($mh);
    return $results;
}

总结

优先选择主题推送,这是最省心高效的方案;如果必须用设备ID列表,就用并行请求或异步队列处理批次,避免串行阻塞导致的延迟。同时修正原代码中未处理批量设备ID的bug,确保请求格式符合FCM要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 15:11:00