博客 / 詳情

返回

PHP 多進程任務 spatie/async 擴展使用説明

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 任務。使用者有可能需要在子進程中加載更多設置。子進程總是從零開始啓動的,所以就需要進行初始化。比如:加載依賴容器、核心類)

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.