Laravel/PHP如何并行运行3个API数据获取函数?含调度器场景
嘿,这个需求在Laravel环境下完全可以实现,而且有几种不同的路子适配不同场景,我给你详细说说:
方案一:Laravel队列(首推!适配调度器场景)
既然你要用Laravel的调度器,那队列绝对是最贴合生态的选择——不仅能实现并行执行,还自带失败重试、任务监控这些贴心功能,长期维护起来超省心。
步骤很简单:
给每个API写队列任务
给每个API的fetch方法单独创建一个队列任务类,比如:// app/Jobs/FetchApi1Data.php namespace App\Jobs; use App\Classes\api1; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; class FetchApi1Data implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public function handle() { // 直接调用你的API类fetch方法 (new api1())->fetch(); } }照着这个模板,再创建
FetchApi2Data和FetchApi3Data两个任务类就行。调度器里分发任务
打开app/Console/Kernel.php,在schedule方法里把三个任务一起分发——队列worker会自动并行处理它们:protected function schedule(Schedule $schedule) { $schedule->call(function () { FetchApi1Data::dispatch(); FetchApi2Data::dispatch(); FetchApi3Data::dispatch(); })->everyMinute(); }划重点:别忘了先配置好队列驱动(比如Redis或者Database),然后启动队列worker:
php artisan queue:work,不然任务只会存在队列里不会执行哦!
方案二:Guzzle并发请求(如果你的API是HTTP接口)
如果你的fetch方法本质就是发送HTTP请求,那直接用Guzzle的并发功能更轻量,不用折腾队列worker,在一个调度任务里就能搞定并行。
首先确保装了Guzzle:composer require guzzlehttp/guzzle
然后写一个自定义命令:
// app/Console/Commands/FetchAllApis.php namespace App\Console\Commands; use GuzzleHttp\Client; use GuzzleHttp\Pool; use GuzzleHttp\Psr7\Request; use Illuminate\Console\Command; class FetchAllApis extends Command { protected $signature = 'fetch:all-apis'; protected $description = '并行抓取所有API数据'; public function handle() { $client = new Client(); // 定义所有要发的请求 $requests = function () { yield new Request('GET', 'https://api1.example.com/data'); // api1的接口 yield new Request('GET', 'https://api2.example.com/data'); // api2的接口 yield new Request('GET', 'https://api3.example.com/data'); // api3的接口 }; // 启动并发池 $pool = new Pool($client, $requests(), [ 'concurrency' => 3, // 同时跑3个请求 'fulfilled' => function ($response, $index) { // 处理每个请求的响应,存到数据库 $data = json_decode($response->getBody(), true); // 根据索引判断是哪个API,调用对应的保存逻辑 match($index) { 0 => (new \App\Classes\api1())->saveToDb($data), 1 => (new \App\Classes\api2())->saveToDb($data), 2 => (new \App\Classes\api3())->saveToDb($data), }; }, 'rejected' => function ($reason, $index) { // 请求失败就记日志,方便排查 \Log::error("第{$index}个API抓取失败: " . $reason->getMessage()); }, ]); // 等待所有请求完成 $promise = $pool->promise(); $promise->wait(); $this->info('所有API抓取任务完成!'); } }
最后在调度器里注册这个命令:
protected function schedule(Schedule $schedule) { $schedule->command('fetch:all-apis')->everyMinute(); }
方案三:PHP多进程(适合非HTTP的耗时任务)
如果你的fetch方法不是HTTP请求,而是比如本地数据处理、第三方SDK调用这类耗时操作,可以用PHP的pcntl扩展实现多进程并行。
直接在调度器里写逻辑就行:
protected function schedule(Schedule $schedule) { $schedule->call(function () { $processes = []; // 启动api1的进程 $pid1 = pcntl_fork(); if ($pid1 == 0) { (new \App\Classes\api1())->fetch(); exit(); // 子进程执行完要退出 } else { $processes[] = $pid1; } // 启动api2的进程 $pid2 = pcntl_fork(); if ($pid2 == 0) { (new \App\Classes\api2())->fetch(); exit(); } else { $processes[] = $pid2; } // 启动api3的进程 $pid3 = pcntl_fork(); if ($pid3 == 0) { (new \App\Classes\api3())->fetch(); exit(); } else { $processes[] = $pid3; } // 等待所有子进程结束 foreach ($processes as $pid) { pcntl_waitpid($pid, $status); } })->everyMinute(); }
注意:这个方法需要服务器安装pcntl扩展,而且只能在CLI环境下运行(调度器本身就是CLI,所以没问题),但Web环境千万别用哦!
最后给你划个重点
- 优先选队列方案:功能全、维护简单,完美适配长期调度的任务。
- 如果是HTTP API,Guzzle并发更轻量,不用额外启动worker。
- 多进程方案适合特殊场景,但需要扩展支持,维护成本稍高。
内容的提问来源于stack exchange,提问作者J.Adams

