Laravel批量二维码生成性能优化求助:单次生成100个耗时2分钟
问题描述
我正在用Laravel开发一套系统,用户可以通过输入框输入数量来生成对应数量的二维码。测试时单次生成100个二维码耗时近2分钟,请问怎么尽可能提升生成速度?感谢帮助!
控制器中的StoreQR方法:
public function storeQR(Request $request) { $numberOfDevices = (int) $request->input('number'); if ($numberOfDevices <= 0) { return back()->withErrors(['number' => '请输入有效的设备数量。']); } for ($i = 0; $i < $numberOfDevices; $i++) { $deviceID = Str::random(8); // 创建设备数据 $devices[] = [ 'deviceId' => $deviceID, ]; } GenerateQRJob::dispatch($devices); return to_route('devices.index'); }
GenerateQRJob任务类:
public function __construct($devices) { $this->devices = $devices; } /** * 执行任务 */ public function handle(): void { $devices = []; foreach ($this->devices as $device){ // 生成二维码和路径 $qr = QrCode::format('png') ->size(285) ->generate(route('devices.qr', $device['deviceId'])); $path = 'img/qr-codes/' . $device['deviceId'] . '.png'; Storage::disk('public')->put($path, $qr); $devices[] = [ 'deviceId' => $device['deviceId'], 'barcode' => $path ]; } DB::table('devices')->insert($devices); }
优化方案
1. 拆分任务为多个子任务并行处理
当前所有设备数据都塞进单个队列任务串行处理,改成按固定数量分块,每个块单独触发一个任务,利用队列多进程并行执行,能大幅压缩总耗时。
修改控制器storeQR方法:
public function storeQR(Request $request) { $numberOfDevices = (int) $request->input('number'); if ($numberOfDevices <= 0) { return back()->withErrors(['number' => '请输入有效的设备数量。']); } // 每10个设备拆成一个任务,可根据服务器性能调整分块大小 collect(range(1, $numberOfDevices))->chunk(10)->each(function ($chunk) { $devices = $chunk->map(function () { return [ 'deviceId' => Str::random(8), ]; })->toArray(); GenerateQRJob::dispatch($devices); }); return to_route('devices.index'); }
注意:启动队列时要开启多个worker进程,比如执行php artisan queue:work --processes=5,让多个任务能同时运行。
2. 替换二维码生成工具为系统命令行工具
PHP原生二维码库的生成速度较慢,换成系统级的qrencode工具,生成效率会提升数倍。
首先安装qrencode:
- Ubuntu/Debian:
sudo apt-get install qrencode - CentOS/RHEL:
sudo yum install qrencode
修改任务类handle方法:
public function handle(): void { $devices = []; // 预先生成路由基础地址,避免重复调用route函数解析路由 $baseQrUrl = url('/devices/qr/'); $storagePath = storage_path('app/public/img/qr-codes/'); // 确保目录存在,避免生成失败 if (!File::exists($storagePath)) { File::makeDirectory($storagePath, 0755, true); } foreach ($this->devices as $device) { $deviceId = $device['deviceId']; $qrContent = $baseQrUrl . $deviceId; $filePath = $storagePath . $deviceId . '.png'; // 调用系统命令生成二维码,-s参数控制尺寸(对应原size(285)) exec("qrencode -s 10 -o {$filePath} '{$qrContent}'"); $devices[] = [ 'deviceId' => $deviceId, 'barcode' => 'img/qr-codes/' . $deviceId . '.png' ]; } DB::table('devices')->insert($devices); }
3. 减少路由解析开销
原代码每次生成二维码都调用route()函数解析路由,换成直接拼接URL的方式,避免重复的路由解析计算,上面的代码已经包含这个优化。
4. 优化文件存储逻辑
直接通过系统命令写入文件,跳过Laravel Storage的封装层,减少IO操作的额外开销;同时提前检查并创建目录,避免每次写入时的目录检查耗时。
5. 配置任务超时时间
在GenerateQRJob类中添加超时配置,避免单个任务因耗时过长被队列终止:
class GenerateQRJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public $timeout = 60; // 单个任务超时时间,单位秒 // ... 其他代码 }
内容的提问来源于stack exchange,提问作者rizkydhani
相关产品推荐
相关产品推荐

