124 lines
3.4 KiB
Markdown
124 lines
3.4 KiB
Markdown
# Concurrency
|
||
|
||
* Introduction
|
||
* Running Concurrent Tasks
|
||
* Deferring Concurrent Tasks
|
||
|
||
## Introduction
|
||
|
||
Sometimes you may need to execute several slow tasks which do not depend on
|
||
one another. In many cases, significant performance improvements can be
|
||
realized by executing the tasks concurrently. Laravel's `Concurrency` facade
|
||
provides a simple, convenient API for executing closures concurrently.
|
||
|
||
#### How it Works
|
||
|
||
Laravel achieves concurrency by serializing the given closures and dispatching
|
||
them to a hidden Artisan CLI command, which unserializes the closures and
|
||
invokes it within its own PHP process. After the closure has been invoked, the
|
||
resulting value is serialized back to the parent process.
|
||
|
||
The `Concurrency` facade supports three drivers: `process` (the default),
|
||
`fork`, and `sync`.
|
||
|
||
The `fork` driver offers improved performance compared to the default
|
||
`process` driver, but it may only be used within PHP's CLI context, as PHP
|
||
does not support forking during web requests. Before using the `fork` driver,
|
||
you need to install the `spatie/fork` package:
|
||
|
||
|
||
|
||
1composer require spatie/fork
|
||
|
||
|
||
composer require spatie/fork
|
||
|
||
The `sync` driver is primarily useful during testing when you want to disable
|
||
all concurrency and simply execute the given closures in sequence within the
|
||
parent process.
|
||
|
||
## Running Concurrent Tasks
|
||
|
||
To run concurrent tasks, you may invoke the `Concurrency` facade's `run`
|
||
method. The `run` method accepts an array of closures which should be executed
|
||
simultaneously in child PHP processes:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\Concurrency;
|
||
|
||
2use Illuminate\Support\Facades\DB;
|
||
|
||
3
|
||
|
||
4[$userCount, $orderCount] = Concurrency::run([
|
||
|
||
5 fn () => DB::table('users')->count(),
|
||
|
||
6 fn () => DB::table('orders')->count(),
|
||
|
||
7]);
|
||
|
||
|
||
use Illuminate\Support\Facades\Concurrency;
|
||
use Illuminate\Support\Facades\DB;
|
||
|
||
[$userCount, $orderCount] = Concurrency::run([
|
||
fn () => DB::table('users')->count(),
|
||
fn () => DB::table('orders')->count(),
|
||
]);
|
||
|
||
To use a specific driver, you may use the `driver` method:
|
||
|
||
|
||
|
||
1$results = Concurrency::driver('fork')->run(...);
|
||
|
||
|
||
$results = Concurrency::driver('fork')->run(...);
|
||
|
||
Or, to change the default concurrency driver, you should publish the
|
||
`concurrency` configuration file via the `config:publish` Artisan command and
|
||
update the `default` option within the file:
|
||
|
||
|
||
|
||
1php artisan config:publish concurrency
|
||
|
||
|
||
php artisan config:publish concurrency
|
||
|
||
## Deferring Concurrent Tasks
|
||
|
||
If you would like to execute an array of closures concurrently, but are not
|
||
interested in the results returned by those closures, you should consider
|
||
using the `defer` method. When the `defer` method is invoked, the given
|
||
closures are not executed immediately. Instead, Laravel will execute the
|
||
closures concurrently after the HTTP response has been sent to the user:
|
||
|
||
|
||
|
||
1use App\Services\Metrics;
|
||
|
||
2use Illuminate\Support\Facades\Concurrency;
|
||
|
||
3
|
||
|
||
4Concurrency::defer([
|
||
|
||
5 fn () => Metrics::report('users'),
|
||
|
||
6 fn () => Metrics::report('orders'),
|
||
|
||
7]);
|
||
|
||
|
||
use App\Services\Metrics;
|
||
use Illuminate\Support\Facades\Concurrency;
|
||
|
||
Concurrency::defer([
|
||
fn () => Metrics::report('users'),
|
||
fn () => Metrics::report('orders'),
|
||
]);
|
||
|