请问下 Laravel 7 如果使用 Command 多线程?

$chapterDetail [‘img’], 是远程同步图片地址,但是数据大约 200W 条 挨条处理是在太慢!服务器 16H 32G 8T 硬件没有任何问题!请问下如何写多线程! 要不然 200w 条数据同步 2 个月时间太长了

  /*同步内容图片*/
        ini_set('memory_limit', '-1');
        foreach (ChapterDetail::all() as $chapterDetail)
        {
            if (strpos($chapterDetail['img'],'http://')===0 || strpos($chapterDetail['img'],'https://')===0)
            {
            $getChapterDetailPicUrlPicUrl = Http::get($chapterDetail['img'])->body();
                $chapterDetailPicUrl='/images/'.now()->timestamp.rand(000000000,999999999).'.jpg';
                Storage::disk('public')->put($chapterDetailPicUrl,$getChapterDetailPicUrlPicUrl);
                $chapterDetail->update(['img'=>'/storage/'.$chapterDetailPicUrl]);
            }
        }
        $this->info('全部图片同步完成');
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
最佳答案
$pids[];

collect(ChapterDetail::all())->chunk(10000)->each(function($chapterDetails) {
    switch ($pids[] = pcntl_fork()) {
        case -1:
           die('子进程创建失败!');
        case 0:
            foreach ($chapterDetails as $chapterDetail)
            {
                if (strpos($chapterDetail['img'],'http://')===0 || strpos($chapterDetail['img'],'https://')===0)
                {
                    $getChapterDetailPicUrlPicUrl = Http::get($chapterDetail['img'])->body();
                    $chapterDetailPicUrl='/images/'.now()->timestamp.rand(000000000,999999999).'.jpg';
                    Storage::disk('public')->put($chapterDetailPicUrl,$getChapterDetailPicUrlPicUrl);
                    $chapterDetail->update(['img'=>'/storage/'.$chapterDetailPicUrl]);
                }
            }
            exit;
        default:
            break;
    }
});

while (count($pids)) {
    $pid = pcntl_waitpid(-1, $status, WNOHANG);
    if ($pid === -1) {
        break;
    }
    $pids = array_filter($pids, fn($id) => $id !== $pid);
}

$this->info('全部图片同步完成');

pcntl_fork 方法只能在 cli 下运行

4年前 评论
pingfan (作者) 4年前
李小明 (楼主) 4年前
讨论数量: 11
$pids[];

collect(ChapterDetail::all())->chunk(10000)->each(function($chapterDetails) {
    switch ($pids[] = pcntl_fork()) {
        case -1:
           die('子进程创建失败!');
        case 0:
            foreach ($chapterDetails as $chapterDetail)
            {
                if (strpos($chapterDetail['img'],'http://')===0 || strpos($chapterDetail['img'],'https://')===0)
                {
                    $getChapterDetailPicUrlPicUrl = Http::get($chapterDetail['img'])->body();
                    $chapterDetailPicUrl='/images/'.now()->timestamp.rand(000000000,999999999).'.jpg';
                    Storage::disk('public')->put($chapterDetailPicUrl,$getChapterDetailPicUrlPicUrl);
                    $chapterDetail->update(['img'=>'/storage/'.$chapterDetailPicUrl]);
                }
            }
            exit;
        default:
            break;
    }
});

while (count($pids)) {
    $pid = pcntl_waitpid(-1, $status, WNOHANG);
    if ($pid === -1) {
        break;
    }
    $pids = array_filter($pids, fn($id) => $id !== $pid);
}

$this->info('全部图片同步完成');

pcntl_fork 方法只能在 cli 下运行

4年前 评论
pingfan (作者) 4年前
李小明 (楼主) 4年前

写个 http 接口,入参为同步的数组索引范围。然后用 Java(Thread+HttpClient)或者 php(curl_multi)写个多线程接口调用,模拟 100 个并发线程即可。 本身宿主机若用 php-fpm,需要开启 100 个以上的子进程。

4年前 评论
李小明 (楼主) 4年前
蔺焕然

填入队列中 走消费模型

4年前 评论
gooogle 4年前
pingfan 4年前
Epona

这种功能都是走队列的,而不是走多线程

4年前 评论

file

4年前 评论

简单粗暴

php arstian spider
php arstian spider
php arstian spider
php arstian spider
.......
4年前 评论
李小明 (楼主) 4年前
小李世界 (作者) 4年前

应该不能叫多线程,可启动多个队列,进行多进程消费。

4年前 评论

外层 foreach 写成 ScheduleJob 负责遍历调度,foreach 内部代码写成 WorkJob 负责处理工作,设置一下连接超时时间。horizon 开 20 个进程就差不多了。

4年前 评论
$pids[];

collect(ChapterDetail::all())->chunk(10000)->each(function($chapterDetails) {
    switch ($pids[] = pcntl_fork()) {
        case -1:
           die('子进程创建失败!');
        case 0:
            foreach ($chapterDetails as $chapterDetail)
            {
                if (strpos($chapterDetail['img'],'http://')===0 || strpos($chapterDetail['img'],'https://')===0)
                {
                    $getChapterDetailPicUrlPicUrl = Http::get($chapterDetail['img'])->body();
                    $chapterDetailPicUrl='/images/'.now()->timestamp.rand(000000000,999999999).'.jpg';
                    Storage::disk('public')->put($chapterDetailPicUrl,$getChapterDetailPicUrlPicUrl);
                    $chapterDetail->update(['img'=>'/storage/'.$chapterDetailPicUrl]);
                }
            }
            exit;
        default:
            break;
    }
});

while (count($pids)) {
    $pid = pcntl_waitpid(-1, $status, WNOHANG);
    if ($pid === -1) {
        break;
    }
    $pids = array_filter($pids, fn($id) => $id !== $pid);
}

$this->info('全部图片同步完成');

pcntl_fork 方法只能在 cli 下运行

4年前 评论
pingfan (作者) 4年前
李小明 (楼主) 4年前

队列,用 supervisor 管理队列,可以支持多进程

4年前 评论