並行處理
📝 此頁面為 Laravel 官方文檔的繁體中文翻譯。查看原始英文版本
並行處理
簡介
有時您可能需要執行幾個彼此不依賴的慢速任務。在許多情況下,透過並行執行任務可以實現顯著的效能改善。Laravel 的 Concurrency facade 提供了一個簡單、方便的 API 來並行執行閉包。
運作原理
Laravel 透過序列化給定的閉包並將它們派發到隱藏的 Artisan CLI 命令來實現並行處理,該命令會反序列化閉包並在其自己的 PHP 進程中呼叫它。閉包被呼叫後,結果值會被序列化回父進程。
Concurrency facade 支援三種驅動程式:process(預設)、fork 和 sync。
fork 驅動程式提供比預設 process 驅動程式更好的效能,但它只能在 PHP 的 CLI 上下文中使用,因為 PHP 不支援在 Web 請求期間進行 fork。使用 fork 驅動程式之前,您需要安裝 spatie/fork 套件:
composer require spatie/fork
sync 驅動程式主要在測試時有用,當您想停用所有並行處理並僅在父進程中按順序執行給定的閉包時。
執行並行任務
要執行並行任務,您可以呼叫 Concurrency facade 的 run 方法。run 方法接受一個閉包陣列,這些閉包應在子 PHP 進程中同時執行:
use Illuminate\Support\Facades\Concurrency;
use Illuminate\Support\Facades\DB;
[$userCount, $orderCount] = Concurrency::run([
fn () => DB::table('users')->count(),
fn () => DB::table('orders')->count(),
]);
要使用特定的驅動程式,可以使用 driver 方法:
$results = Concurrency::driver('fork')->run(...);
或者,要變更預設的並行處理驅動程式,您應該透過 config:publish Artisan 命令發佈 concurrency 設定檔,並更新檔案中的 default 選項:
php artisan config:publish concurrency
命名結果
如果您希望透過名稱而非位置來存取並行任務結果,可以提供一個關聯陣列的閉包。每個結果將使用與其對應閉包相同的索引鍵返回:
use Illuminate\Support\Facades\Concurrency;
use Illuminate\Support\Facades\DB;
$results = Concurrency::run([
'users' => fn () => DB::table('users')->count(),
'orders' => fn () => DB::table('orders')->count(),
]);
$userCount = $results['users'];
$orderCount = $results['orders'];
任務逾時
使用 process 驅動程式(預設)時,您可以透過向 run 方法提供逾時時間來指定並行任務在被終止之前允許運行的最大秒數:
use Illuminate\Support\Facades\Concurrency;
use Illuminate\Support\Facades\DB;
[$userCount, $orderCount] = Concurrency::run([
fn () => DB::table('users')->count(),
fn () => DB::table('orders')->count(),
], timeout: 30);
如果您偏好更具表達性的逾時定義,也可以提供 CarbonInterval 實例:
use Illuminate\Support\Facades\Concurrency;
use function Illuminate\Support\seconds;
Concurrency::run([...], timeout: seconds(30));
延遲並行任務
如果您想並行執行一組閉包,但對這些閉包返回的結果不感興趣,應該考慮使用 defer 方法。當 defer 方法被呼叫時,給定的閉包不會立即執行。相反,Laravel 將在 HTTP 響應已傳送到使用者後並行執行這些閉包:
use App\Services\Metrics;
use Illuminate\Support\Facades\Concurrency;
Concurrency::defer([
fn () => Metrics::report('users'),
fn () => Metrics::report('orders'),
]);