laravel11 Concurrency并发器

有时您可能需要执行几个相互独立的慢任务。在许多情况下,通过并发执行任务可以实现显著的性能提升。Laravel 的 Concurrency 门面提供了一个简单、方便的 API,用于并发执行闭包。

工作原理

Laravel 通过序列化给定的闭包并将其分派到一个隐藏的 Artisan CLI 命令来实现并发,该命令在其自己的 PHP 进程中反序列化闭包并调用它。在闭包被调用后,结果值会被序列化回父进程。
Concurrency 门面支持三种驱动程序:process(默认)、fork 和 sync。

fork 驱动程序相比于默认的 process 驱动程序提供了更好的性能,但只能在 PHP 的 CLI 上下文中使用,因为 PHP 不支持在 Web 请求期间进行分叉。在使用 fork 驱动程序之前,您需要安装 spatie/fork 包:

composer require spatie/fork

sync 驱动程序主要在测试期间有用,当您希望禁用所有并发并简单地在父进程中顺序执行给定的闭包时。

功能尝试

测试代码

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Concurrency;

class SendEmails extends Command
{
    protected $signature = 'app:send-emails';

    public function handle()
    {
        // 切换 子进程启动 fork process
        [$a, $b] = Concurrency::driver('fork')
            ->run([
            fn () => $this->test(5),
            fn () =>  $this->test(10),
        ]);
        dd($a, $b);
    }

    private function test(int $a): int
    {
        sleep($a);
        return $a;
    }
}

执行结果

// process 方式

root@653d4ba60192:/var/www# ps -axjf
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    0    33    33    33 pts/1      749 Ss       0   0:00 bash
   33   749   749    33 pts/1      749 S+       0   0:00  \_ php artisan app:send-emails
  749   752   749    33 pts/1      749 S+       0   0:00      \_ sh -c '/usr/bin/php8.3' 'artisan' invoke-serialized-clo
  752   754   749    33 pts/1      749 S+       0   0:00      |   \_ /usr/bin/php8.3 artisan invoke-serialized-closure
  749   753   749    33 pts/1      749 S+       0   0:00      \_ sh -c '/usr/bin/php8.3' 'artisan' invoke-serialized-clo
  753   755   749    33 pts/1      749 S+       0   0:00          \_ /usr/bin/php8.3 artisan invoke-serialized-closure

// fork 方式

root@653d4ba60192:/var/www# ps -axjf
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    0   402   402   402 pts/2      788 Ss       0   0:00 bash
  402   788   788   402 pts/2      788 R+       0   0:00  \_ ps -axjf
    0    33    33    33 pts/1      779 Ss       0   0:00 bash
   33   779   779    33 pts/1      779 S+       0   0:00  \_ php artisan app:send-emails
  779   781   779    33 pts/1      779 S+       0   0:00      \_ php artisan app:send-emails
  779   782   779    33 pts/1      779 S+       0   0:00      \_ php artisan app:send-emails

process与fork启动方式比较

两种方式都会等待所有的子方法都执行完成后输出结果并结束脚本,只是启动子进程的方式不同。

一、启动方式的本质区别

  1. 第一种情况(通过 sh -c 间接启动)

    • 特征:父进程 php artisan app:send-emails(PID 749)通过 sh -c 命令启动子进程(如 PID 752、753)。
    • 原理sh -c 会创建一个新的 Shell 进程来执行后续命令(如 /usr/bin/php8.3),因此子进程的父进程是 Shell(PID 752/753),而非直接继承自原 PHP 进程。
    • 典型场景:通过 system()exec() 等函数调用外部命令时,若未绕过 Shell,则会生成中间 Shell 进程。
  2. 第二种情况(直接由 PHP 进程启动)

    • 特征:父进程 php artisan app:send-emails(PID 779)直接创建子进程(PID 781、782),没有中间 Shell 进程。
    • 原理:使用 fork() + exec() 直接加载 PHP 可执行文件,子进程与原 PHP 进程形成严格的父子关系。
    • 典型场景:通过编程语言的进程管理接口(如 PHP 的 pcntl_fork())直接创建子进程,或调用可执行文件时绕过 Shell。

二、进程树结构的差异

特征 第一种情况 第二种情况
父进程关系 子进程的父进程是 Shell(PID 752/753) 子进程的父进程是原 PHP 进程(PID 779)
进程层级 多层级(PHP → Shell → PHP) 单层级(PHP → PHP)
资源消耗 更高(额外创建 Shell 进程) 更低(无中间进程)
信号传递 需通过 Shell 中转,可能延迟或丢失 直接由父进程管理,更可靠
环境变量继承 可能受 Shell 环境变量影响 直接继承父进程环境变量

三、技术实现对比

  1. Shell 介入的影响

    • 第一种方式依赖 Shell 解释命令,可能引入环境变量解析、通配符扩展等副作用。
    • 第二种方式完全由程序控制,避免 Shell 的不可预测行为,安全性更高。
  2. 进程组与会话管理

    • 在第一种情况中,子进程的 PGID(进程组 ID)与父进程不同(如 PID 749 的 PGID 是 749,而子进程 PID 752 的 PGID 是 749),但会话(SID)保持一致,说明它们属于同一会话但可能跨进程组。
    • 第二种情况中,所有子进程的 PGID 和 SID 均与父进程一致(如 PID 779、781、782 的 PGID 和 SID 均为 779 和 33),表明它们属于同一进程组和会话,便于统一管理(如发送信号到整个组)。
  3. 调试复杂度

    • 第一种方式因存在中间 Shell 进程,调试时需追踪多级调用链(如通过 pstreeps -ejH 查看层级)。
    • 第二种方式结构清晰,可直接通过 gdb 附加到父进程分析子进程行为。
  4. 适用场景

    • 间接启动:适合需要动态生成命令(如拼接参数)或依赖 Shell 功能的场景。
    • 直接启动:适合高性能、高稳定性的后台任务(如守护进程或并发 Worker)。

总结

  • process 驱动
    适用 Web/命令行环境,兼容性好但性能中等
  • fork 驱动
    CLI 专用,高性能,需安装扩展包
  • sync 驱动
    用于测试场景的同步模式

评论

  1. 稻草人
    Windows Chrome 134.0.0.0
    9 月前
    2025-3-19 13:43:58

    厉害了我的然

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇