从Java转PHP:如何实现PHP任务并行处理图片缩放与存储?
PHP实现并行任务处理(图片缩放+S3存储)
针对你的图片缩放后上传S3的并行需求,下面是几种简便且实用的实现方案,适配不同CPU核心数的机器:
一、原生多进程(pcntl扩展)
适合有pcntl扩展权限的环境(比如CLI、自建服务器),直接利用多核CPU创建子进程并行处理任务。
实现示例:
<?php // 假设已获取到用户上传的所有临时图片路径 $imagePaths = ['/tmp/upload_img1.jpg', '/tmp/upload_img2.jpg', '/tmp/upload_img3.jpg']; $childPids = []; foreach ($imagePaths as $imgPath) { $pid = pcntl_fork(); if ($pid == -1) { die('创建子进程失败'); } elseif ($pid == 0) { // 子进程:处理图片缩放+上传S3 // 1. 缩放图片(GD扩展示例) $srcImg = imagecreatefromjpeg($imgPath); $width = imagesx($srcImg); $height = imagesy($srcImg); $newWidth = 800; $newHeight = intval($height * ($newWidth / $width)); $newImg = imagecreatetruecolor($newWidth, $newHeight); imagecopyresampled($newImg, $srcImg, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height); $resizedPath = '/tmp/resized_' . basename($imgPath); imagejpeg($newImg, $resizedPath); imagedestroy($srcImg); imagedestroy($newImg); // 2. 上传到S3(AWS SDK示例) $s3 = new Aws\S3\S3Client([ 'version' => 'latest', 'region' => 'your-region', ]); $s3->putObject([ 'Bucket' => 'your-bucket', 'Key' => 'products/' . basename($resizedPath), 'SourceFile' => $resizedPath, ]); // 子进程处理完退出 exit(0); } else { // 父进程记录子进程PID,可选等待所有子进程完成再返回 $childPids[] = $pid; } } // 可选:等待所有子进程处理完成 foreach ($childPids as $pid) { pcntl_waitpid($pid, $status); }
注意:
- Web环境(如FPM)通常禁用pcntl扩展,这种情况优先考虑其他方案。
- 子进程会复制父进程资源,注意清理临时文件避免泄漏。
二、协程框架(Swoole/Workerman)
协程是轻量级并发方式,资源消耗远低于进程,适合IO密集型任务(图片处理+S3上传均为IO操作),能高效利用多核CPU。
Swoole协程实现示例:
<?php Co\run(function () { $imagePaths = ['/tmp/upload_img1.jpg', '/tmp/upload_img2.jpg', '/tmp/upload_img3.jpg']; $coroutines = []; foreach ($imagePaths as $imgPath) { // 为每张图片创建独立协程 $coroutines[] = go(function () use ($imgPath) { // 1. 图片缩放逻辑(同GD示例) $srcImg = imagecreatefromjpeg($imgPath); $width = imagesx($srcImg); $height = imagesy($srcImg); $newWidth = 800; $newHeight = intval($height * ($newWidth / $width)); $newImg = imagecreatetruecolor($newWidth, $newHeight); imagecopyresampled($newImg, $srcImg, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height); $resizedPath = '/tmp/resized_' . basename($imgPath); imagejpeg($newImg, $resizedPath); imagedestroy($srcImg); imagedestroy($newImg); // 2. 协程化上传S3(需AWS SDK支持协程) $s3 = new Aws\S3\S3Client([ 'version' => 'latest', 'region' => 'your-region', 'http' => [ 'handler' => new Aws\SwooleCoroutineHandler(), ], ]); $s3->putObject([ 'Bucket' => 'your-bucket', 'Key' => 'products/' . basename($resizedPath), 'SourceFile' => $resizedPath, ]); }); } // 等待所有协程执行完成 foreach ($coroutines as $co) { $co->join(); } });
优势:
- 单进程内即可实现高并发,开启Swoole多进程模式可自动利用多核CPU。
- 代码逻辑接近同步写法,学习成本低。
三、异步队列+多进程Worker(Web环境首选)
如果部署在常规Web环境(如FPM),无法使用pcntl或协程,可采用「任务队列+后台Worker」的方式,避免阻塞用户请求,同时实现并行处理。
步骤:
- 上传图片后入队:用户上传图片后,将任务信息存入Redis队列
<?php // Web请求中:接收上传图片,保存到临时目录后入队 $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $task = [ 'img_path' => '/tmp/uploaded_img.jpg', 's3_key' => 'products/' . uniqid() . '.jpg', 'resize_width' => 800, ]; $redis->lpush('product_image_tasks', json_encode($task)); // 直接返回用户响应,无需等待处理完成 echo json_encode(['code' => 200, 'msg' => '上传成功,图片处理中']);
- 多进程Worker消费队列:编写CLI脚本,启动对应CPU核心数的进程消费任务
<?php // worker.php $workerNum = 4; // 根据CPU核心数设置,比如n核设n个进程 $childPids = []; // 创建多个Worker子进程 for ($i = 0; $i < $workerNum; $i++) { $pid = pcntl_fork(); if ($pid == -1) { die('创建Worker失败'); } elseif ($pid == 0) { $redis = new Redis(); $redis->connect('127.0.0.1', 6379); echo "Worker {$i} 启动,PID: " . getmypid() . PHP_EOL; while (true) { // 阻塞式从队列取任务 $taskStr = $redis->brpop('product_image_tasks', 0); if ($taskStr) { $task = json_decode($taskStr[1], true); // 执行图片缩放+上传S3(同前面的逻辑) // ... 省略图片缩放代码 ... // ... 省略S3上传代码 ... echo "Worker {$i} 完成任务:{$task['img_path']}" . PHP_EOL; } } exit(0); } else { $childPids[] = $pid; } } // 父进程等待子进程,防止意外退出 foreach ($childPids as $pid) { pcntl_waitpid($pid, $status); }
- 启动Worker:在服务器后台运行
php worker.php,可使用supervisor管理进程,防止意外退出。
优势:
- 完全兼容Web环境,不阻塞用户请求。
- Worker进程数可根据CPU核心数调整,最大化利用硬件资源。
内容的提问来源于stack exchange,提问作者Adelin
相关产品推荐
相关产品推荐

