头图

spatie/async 是为 PHP 基于 PCNTL 扩展开发的,能够使用多进程处理任务。

项目地址

spatie/async

常规使用示例

对象形式

use Spatie\Async\Pool;

$pool = Pool::create()
// 选择执行 PHP
->withBinary('/path/to/php')
// 可同时运行的最大进程数
->concurrency(20)
// 进程完成所需的最长时间(以秒为单位),支持小数点位置以实现更细粒度的超时
->timeout(15)
// 配置应该使用哪个自动加载器子流程
->autoload(__DIR__ . '/../../vendor/autoload.php')
// 配置循环应该休眠多长时间,然后重新检查进程状态(以微秒为单位)。
->sleepTime(50000);

foreach ($things as $thing) {
    $pool->add(function () use ($thing) {
        // 添加任务
    })->then(function ($output) {
        // 任务执行完毕回调函数,返回结果会以变量 $output 注入回调函数

        // $pool->stop(); 提前终止 pool 任务池执行,终止后想要继续使用则需要重新创建任务池
    })->catch(function (MyException $e) {
        // 捕捉异常
    })
    ->catch(function (Exception $e) {
        // / 捕捉异常,支持不做多个
    })->timeout(function () {
        // 超时处理
    });
}
$pool->wait();

函数使用方式

use Spatie\Async\Pool;

$pool = Pool::create();

foreach (range(1, 5) as $i) {
    $pool[] = async(function () {
        usleep(random_int(10, 1000));
        return 2;
    })->then(function (int $output) {
        echo $output.PHP_EOL;
    });
}

await($pool);

任务类使用方式

需要创建任务类,并继承类 Spatie\Async\Task,任务执行会调用 task 任务类的 run() 方法

use Spatie\Async\Task;

class MyTask extends Task
{
    public function configure()
    {
        // 加载配置,如:依赖容器、核心类加载
    }

    public function run()
    {
        // 任务工作程序
    }
}

// Add the task to the pool
$pool = Pool::create();
$pool->add(new MyTask());
$pool->wait();

补充:在 Laravel 中使用注意

如果没有加载 bootstrap/app.php 配置,则无法使用 Laravel 提供的类方法。比如:使用 Illuminate\Support\Facades\Log 时会报异常 Exception: A facade root has not been set

未加载 bootstrap/app.php 示例

<?php
namespace App\Task;

use Spatie\Async\Task;
use Illuminate\Support\Facades\Log;

class EmployTask extends Task
{
    public function configure()
    {
    }

    public function run()
    {
        $datetime = date('Y-m-d H:i:s');
        Log::error('EmployTask',['type' => 'use Log','msg' => '未加载 bootstrap/app.php', 'datetime' => $datetime]);
    }
}

报错信息如下

[2022-03-28 17:26:17] local.ERROR: EmployTask {"msg":"A facade root has not been set.

#0 /www/wwwroot/laravel/app/Task/EmployTask.php(23): Illuminate\\Support\\Facades\\Facade::__callStatic()
#1 /www/wwwroot/laravel/app/Task/EmployTask.php(17): App\\Task\\EmployTask->test()
#2 /www/wwwroot/laravel/vendor/spatie/async/src/Task.php(15): App\\Task\\EmployTask->run()
#3 /www/wwwroot/laravel/vendor/spatie/async/src/Runtime/ChildRuntime.php(26): Spatie\\Async\\Task->__invoke()
#4 {main}"} 

加载 bootstrap/app.php 示例

<?php
namespace App\Task;

use Spatie\Async\Task;
use Illuminate\Support\Facades\Log;

class EmployTask extends Task
{
    public function configure()
    {
        $app = require __DIR__.'/../../bootstrap/app.php';
        $kernel = $app->make(\Illuminate\Contracts\Console\Kernel::class);
        $kernel->bootstrap();
    }

    public function run()
    {
        $datetime = date('Y-m-d H:i:s');
        Log::error('EmployTask',['type' => 'use Log','msg' => '加载 bootstrap/app.php', 'datetime' => $datetime]);
    }
}


// 也可以这样使用

$pool[] = async(function () use ($result, $syncemployRepository) {
    $app = require __DIR__.'/../../../../bootstrap/app.php';
    $kernel = $app->make(\Illuminate\Contracts\Console\Kernel::class);
    $kernel->bootstrap();
    // 任务代码
})->then(function ($output) use($pool) {
    $pool->stop();
})->catch(function (Exception $e) {
    Log::error('createauth_sync', ['msg' => $e->getMessage()]);
});

记录日志如下

[2022-03-28 17:28:26] local.ERROR: EmployTask {"type":"use Log","msg":"加载 bootstrap/app.php","datetime":"2022-03-28 17:28:26"} 

原因说明

作者在文档中有说明 Besides using closures, you can also work with a Task. A Task is useful in situations where you need more setup work in the child process. Because a child process is always bootstrapped from nothing, chances are you'll want to initialise eg. the dependency container before executing the task. The Task class makes this easier to do.(除了可以使用闭包,还可以使用 Task 任务。使用者有可能需要在子进程中加载更多设置。子进程总是从零开始启动的,所以就需要进行初始化。比如:加载依赖容器、核心类)


其名
10 声望2 粉丝

Hello World!