PHP处理16k+用户大数组:如何用延迟任务优化推送性能
处理大规模推送通知的延迟任务方案
一、用消息队列异步拆分任务
把每个用户的推送请求拆成独立任务扔进消息队列(比如Redis List、RabbitMQ),再启动多个消费者进程/线程分批处理,既能控制并发量,又不会一次性占满服务器资源。
示例代码(PHP+Redis):
生产者(拆分任务入队):
foreach ($users as $user) { $task = json_encode([ 'user_id' => $user['id'], 'token' => $user['push_token'], 'content' => '你的推送内容' ]); // 推入Redis队列 $redis->lPush('push_tasks', $task); }
消费者(后台持续处理任务):
while (true) { // 阻塞式取任务,避免空轮询浪费资源 $task = $redis->brPop('push_tasks', 0); if ($task) { $data = json_decode($task[1], true); // 调用推送接口 send_push($data['user_id'], $data['token'], $data['content']); // 每次处理后休眠100毫秒,降低CPU占用 usleep(100000); } }
二、定时任务分批+延迟续跑
不用一次性拆分所有任务,而是通过定时任务每次处理一小批(比如100条),处理完自动触发下一批,或者固定间隔执行。
示例(Cron+PHP):
- 数据库存一个
last_processed_id记录已处理的最后一个用户ID - 定时脚本逻辑:
$batch_size = 100; $last_id = get_last_processed_id(); // 取未处理的100条用户 $users = $db->query("SELECT * FROM users WHERE id > $last_id LIMIT $batch_size")->fetchAll(); foreach ($users as $user) { send_push($user['id'], $user['push_token'], '推送内容'); update_last_processed_id($user['id']); } // 如果当前批次满100条,说明还有未处理用户,1分钟后自动续跑 if (count($users) == $batch_size) { exec('sleep 60 && php /path/to/push_script.php > /dev/null 2>&1 &'); }
三、异步任务+批次延迟调度
用语言自带的异步能力,分批次处理并设置延迟,给服务器留足喘息时间。
示例(Node.js):
const users = require('./user_list.json'); const batch_size = 50; let current_index = 0; function process_batch() { const batch = users.slice(current_index, current_index + batch_size); if (batch.length === 0) return; // 并行处理当前批次推送 Promise.all(batch.map(user => send_push(user.id, user.token, '推送内容'))) .then(() => { current_index += batch_size; // 延迟2秒处理下一批 setTimeout(process_batch, 2000); }) .catch(err => console.log('推送失败:', err)); } // 启动任务 process_batch();
四、优先用服务商批量推送接口
绝大多数推送服务商(FCM、APNs、个推等)都支持批量推送,把多个用户打包成一个请求发送,能大幅减少HTTP请求次数,降低服务器负载。
示例(FCM批量推送):
// 提取所有用户的推送令牌 $tokens = array_column($users, 'fcm_token'); // 按服务商限制拆分批次(FCM一次最多500个令牌) $token_batches = array_chunk($tokens, 500); foreach ($token_batches as $batch) { $payload = [ 'registration_ids' => $batch, 'notification' => [ 'title' => '通知标题', 'body' => '通知内容' ] ]; $ch = curl_init('https://fcm.googleapis.com/fcm/send'); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: key=你的FCM密钥', 'Content-Type: application/json' ]); curl_exec($ch); curl_close($ch); // 延迟1秒避免触发接口限流 sleep(1); }
内容的提问来源于stack exchange,提问作者Aman Kamboj
相关产品推荐
相关产品推荐

