spatie / fork
用于在PHP中并发运行代码的轻量级解决方案
Requires
- php: ^8.0
- ext-pcntl: *
- ext-sockets: *
Requires (Dev)
- nesbot/carbon: ^2.66
- pestphp/pest: ^1.23
- phpunit/phpunit: ^9.5
- spatie/ray: ^1.10
README
此包使得在PHP中并发运行代码变得容易。幕后,通过将主PHP进程分解为一个或多个子任务来实现并发。
在这个例子中,我们将调用一个假设的慢速API,所有三个闭包将同时运行。
use Spatie\Fork\Fork; $results = Fork::new() ->run( fn () => (new Api)->fetchData(userId: 1), fn () => (new Api)->fetchData(userId: 2), fn () => (new Api)->fetchData(userId: 3), ); $results[0]; // fetched data of user 1 $results[1]; // fetched data of user 2 $results[2]; // fetched data of user 3
内部工作原理
✨ 在这个 YouTube视频 中,我们解释了该包是如何在内部工作的。
支持我们
我们投入了大量资源来创建 一流的开放式源代码包。您可以通过 购买我们的付费产品之一 来支持我们。
我们非常感谢您从家乡寄给我们明信片,并说明您正在使用我们的哪些包。您可以在 我们的联系页面 上找到我们的地址。我们将所有收到的明信片发布在 我们的虚拟明信片墙上。
要求
此包需要PHP 8和默认安装在许多Unix和Mac系统中的 pcntl 扩展。
❗️ pcntl 只在CLI进程中工作,不在Web环境中工作。❗️ Alpine Linux需要 posix 来正确处理进程终止。
安装
您可以通过composer安装此包
composer require spatie/fork
用法
您可以将任意数量的闭包传递给 run
。它们将并发运行。run
函数将返回一个包含执行闭包返回值的数组。
use Spatie\Fork\Fork; $results = Fork::new() ->run( function () { sleep(1); return 'result from task 1'; }, function () { sleep(1); return 'result from task 2'; }, function () { sleep(1); return 'result from task 3'; }, ); // this code will be reached this point after 1 second $results[0]; // contains 'result from task 1' $results[1]; // contains 'result from task 2' $results[2]; // contains 'result from task 3'
在每个闭包前后运行代码
如果您需要在每个传递给 run
的可调用项前后执行一些代码,您可以将一个可调用项传递给 before
或 after
方法。这个传递的可调用项将在子进程中执行,在传递给 run
的可调用项执行之前或之后。
在子任务中使用 before
和 after
在这个例子中,我们将使用Laravel Eloquent模型从数据库中获取值。为了让子任务使用DB,需要重新连接到DB。传递给 before
的闭包将在为传递给 run
的闭包创建的所有子任务中运行。
use App\Models\User; use Illuminate\Support\Facades\DB; use Spatie\Fork\Fork; Fork::new() ->before(fn () => DB::connection('mysql')->reconnect()) ->run( fn () => User::find(1)->someLongRunningFunction(), fn () => User::find(2)->someLongRunningFunction(), );
如果您需要在子任务的可调用项运行后执行一些清理工作,您可以在 Spatie\Fork\Fork
实例上使用 after
方法。
在父任务中使用 before
和 after
如果您需要让传递给 before
或 after
的可调用项在父任务中运行,则需要将此可调用项传递给 parent
参数。
use App\Models\User; use Illuminate\Support\Facades\DB; use Spatie\Fork\Fork; Fork::new() ->before( parent: fn() => echo 'this runs in the parent task' ) ->run( fn () => User::find(1)->someLongRunningFunction(), fn () => User::find(2)->someLongRunningFunction(), );
您还可以传递不同的闭包,以便在子任务和父任务中运行
use Spatie\Fork\Fork; Fork::new() ->before( child: fn() => echo 'this runs in the child task', parent: fn() => echo 'this runs in the parent task', ) ->run( fn () => User::find(1)->someLongRunningFunction(), fn () => User::find(2)->someLongRunningFunction(), );
返回数据
所有输出数据都收集到一个数组中,并在所有子任务完成后立即可用。在这个例子中,$results
将包含三个条目
$results = Fork::new() ->run( fn () => (new Api)->fetchData(userId: 1), fn () => (new Api)->fetchData(userId: 2), fn () => (new Api)->fetchData(userId: 3), );
输出也适用于 after
回调,每当子任务完成后都会调用,但不是在完全结束时
$results = Fork::new() ->after( child: fn (int $i) => echo $i, // 1, 2 and 3 parent: fn (int $i) => echo $i, // 1, 2 and 3 ) ->run( fn () => 1, fn () => 2, fn () => 3, );
最后,子任务的返回值使用 PHP 内置的 serialize
方法进行序列化。这意味着你可以返回任何 PHP 中可以序列化的东西,包括对象
$result = Fork::new() ->run( fn () => new DateTime('2021-01-01'), fn () => new DateTime('2021-01-02'), );
配置并发
默认情况下,所有可调用项都将并行运行。但是,你可以配置最大并发进程数
$results = Fork::new() ->concurrent(2) ->run( fn () => 1, fn () => 2, fn () => 3, );
在这种情况下,前两个函数将立即运行,一旦其中之一完成,最后一个也将开始运行。
测试
composer test
变更日志
请参阅 变更日志 了解最近有哪些更改。
贡献
请参阅 贡献指南 了解详细信息。
安全漏洞
请查阅 我们的安全策略 了解如何报告安全漏洞。
鸣谢
许可协议
MIT 许可协议(MIT)。请参阅 许可文件 了解更多信息。