Documentation Index
Fetch the complete documentation index at: https://kawax.biz/llms.txt
Use this file to discover all available pages before exploring further.
並行処理とは
複数の外部APIへのリクエストやデータベースの集計処理など、互いに依存しない複数の処理を順番に実行すると、合計時間はそれぞれの処理時間の合計になります。
これらを同時に実行すれば、合計時間は最も時間のかかる処理1件分に短縮できます。
Laravelの Concurrency ファサードは、こうした並行実行をシンプルなAPIで実現します。
Concurrency ファサードはLaravel 11で導入され、Laravel 13でも引き続き利用できます。
デフォルトドライバは子PHPプロセスを使用するため、追加パッケージなしで動作します。
仕組み
Concurrency ファサードは、渡されたクロージャをシリアライズして隠しArtisanコマンドに送り、それぞれ別のPHPプロセスとして実行します。
処理が完了すると戻り値をシリアライズして親プロセスに返します。
3つのドライバが用意されています。
| ドライバ | 説明 |
|---|
process | デフォルト。子PHPプロセスを起動して実行する。Webリクエストでも動作する |
fork | spatie/fork パッケージが必要。プロセスをforkするためCLIのみで動作するが高速 |
sync | 並行実行せず順番に実行する。テスト用途に向いている |
基本的な使い方
Concurrency::run()
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(),
]);
配列のデストラクチャリングで各結果を変数に受け取れます。
クロージャの順番と戻り値の順番は一致します。
ドライバの指定
特定のドライバを使いたい場合は driver() メソッドで指定します。
$results = Concurrency::driver('fork')->run([
fn () => fetchFromApiA(),
fn () => fetchFromApiB(),
]);
デフォルトのドライバを変更するには、設定ファイルを公開して default オプションを更新します。
php artisan config:publish concurrency
forkドライバの使い方
fork ドライバは process ドライバよりも高速ですが、PHPのCLI環境(Artisanコマンドやキューワーカー)でのみ使用できます。
Webリクエストの中では使用できません。
使用前に spatie/fork パッケージをインストールしてください。
composer require spatie/fork
fork ドライバはWebリクエスト中では動作しません。Artisanコマンドやキューワーカーの中で並行処理を行いたいときに使用してください。
実行結果を受け取らない: Concurrency::defer()
処理の結果には関心がなく、HTTPレスポンスを返した後にバックグラウンドで実行したい場合は defer() メソッドを使います。
use App\Services\Metrics;
use Illuminate\Support\Facades\Concurrency;
Concurrency::defer([
fn () => Metrics::report('users'),
fn () => Metrics::report('orders'),
]);
defer() を呼んだ時点ではクロージャは実行されません。
HTTPレスポンスがユーザーに送信された後に、並行して実行されます。
分析データの記録やキャッシュのウォームアップなど、ユーザーを待たせる必要のない処理に defer() は最適です。
実践例: 複数の外部APIを同時に呼び出す
ECサイトのダッシュボードで、在庫管理API・売上集計API・配送状況APIの3つから情報を取得する例を考えます。
順次実行(改善前)
use Illuminate\Support\Facades\Http;
public function dashboard(): array
{
// 各リクエストが順番に完了するまで待つ(合計約3秒)
$inventory = Http::get('https://api.example.com/inventory')->json();
$sales = Http::get('https://api.example.com/sales')->json();
$shipping = Http::get('https://api.example.com/shipping')->json();
return compact('inventory', 'sales', 'shipping');
}
並行実行(改善後)
use Illuminate\Support\Facades\Concurrency;
use Illuminate\Support\Facades\Http;
public function dashboard(): array
{
// 3つのリクエストを同時に実行(最も遅いAPI1件分の時間で完了)
[$inventory, $sales, $shipping] = Concurrency::run([
fn () => Http::get('https://api.example.com/inventory')->json(),
fn () => Http::get('https://api.example.com/sales')->json(),
fn () => Http::get('https://api.example.com/shipping')->json(),
]);
return compact('inventory', 'sales', 'shipping');
}
各APIが1秒かかる場合、順次実行では合計3秒かかりますが、並行実行では約1秒で完了します。
複数のデータベース集計を同時実行する
use Illuminate\Support\Facades\Concurrency;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Cache;
public function statistics(): array
{
[$totalUsers, $activeUsers, $totalOrders, $revenue] = Concurrency::run([
fn () => DB::table('users')->count(),
fn () => DB::table('users')->where('active', true)->count(),
fn () => DB::table('orders')->count(),
fn () => DB::table('orders')->sum('total_amount'),
]);
return [
'total_users' => $totalUsers,
'active_users' => $activeUsers,
'total_orders' => $totalOrders,
'revenue' => $revenue,
];
}
データベース集計に process ドライバを使う場合、子プロセスごとに新しいデータベース接続が確立されます。
同時実行数が多い場合はデータベースの最大接続数に注意してください。
テスト時の設定
テスト環境では sync ドライバを使うと、クロージャを順番に実行します。
並行実行によるプロセスの立ち上げコストがなく、テストが高速になります。
// tests/Feature/DashboardTest.php
use Illuminate\Support\Facades\Concurrency;
public function test_dashboard_returns_correct_data(): void
{
Concurrency::fake(); // syncドライバに切り替える
$response = $this->get('/dashboard');
$response->assertStatus(200);
}
または .env.testing でデフォルトドライバを変更する方法もあります。
注意事項
クロージャはシリアライズされて子プロセスに渡されます。
シリアライズできないオブジェクト(データベース接続、ファイルハンドル、リソースなど)をクロージャの外側からキャプチャすることはできません。
必要なオブジェクトはクロージャの中で新たに生成してください。// NG: 外側のEloquentモデルをキャプチャ
$user = User::find(1);
Concurrency::run([
fn () => $user->orders()->count(), // シリアライズできない可能性がある
]);
// OK: クロージャの中でデータを取得する
$userId = 1;
Concurrency::run([
fn () => Order::where('user_id', $userId)->count(),
]);
process ドライバは子プロセスの起動コストがあるため、処理自体が非常に短い(数ミリ秒以下)場合は並行実行しても速くならないことがあります。
HTTPリクエストやデータベース集計など、処理に100ms以上かかるものを並行化するときに効果を発揮します。
fork ドライバはPHPのプロセスをforkするため、Webリクエスト中(FPMやApache)では使用できません。
Artisanコマンドやキューワーカーの中でのみ使用してください。
並行実行中に例外が発生すると、run() は例外を再スローします。
個々の処理で例外が発生しても他の処理を継続させたい場合は、クロージャの中で try/catch を使ってください。Concurrency::run([
function () {
try {
return Http::get('https://api.example.com/data')->json();
} catch (\Exception $e) {
return null; // 失敗時はnullを返す
}
},
]);
次のステップ
キューとジョブ
処理をバックグラウンドで非同期実行するキューとジョブの使い方を学ぶ