vxm / laravel-async
为 Laravel 提供一种简单的方式来异步执行代码
Requires
- php: ^8.1
- illuminate/support: ^10.0|^11.0
- spatie/async: ^1.6
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0
- symplify/easy-coding-standard: ^11.2|^12.1
README
Laravel Async
关于它
这是一个提供简单方式运行基于 Spatie Async 包装器的 Laravel 应用程序的异步和并行代码的包。
安装
使用 Composer 安装 Laravel Async
composer require vxm/laravel-async
该包将自动注册自己。
您可以使用以下命令发布配置文件(可选):
php artisan vendor:publish --provider="VXM\Async\AsyncServiceProvider" --tag="config"
这是发布配置文件的内容
return [ /* * The PHP binary will be use in async processes. */ 'withBinary' => PHP_BINARY, /* * Maximum concurrency async processes. */ 'concurrency' => 20, /* * Async process timeout. */ 'timeout' => 15, /* * Sleep (micro-second) time when waiting async processes. */ 'sleepTime' => 50000, /* * Default output length of async processes. */ 'defaultOutputLength' => 1024 * 10, /* * An autoload script to boot composer autoload and Laravel application. * Default null meaning using an autoload of this package. */ 'autoload' => null, ];
用法
运行异步代码
安装后,现在您可以通过 Async
门面尝试运行异步代码
use VXM\Async\AsyncFacade as Async; for ($i = 1; $i < 20; $i++) { Async::run(function () use ($i) { sleep(1); return $i; }); } var_dump(implode(', ', Async::wait())); // Output value may be like: // string(65) "5, 2, 1, 14, 4, 6, 7, 8, 19, 16, 12, 18, 13, 3, 10, 9, 11, 17, 15"
异步作业可以是可调用类、匿名函数或 Laravel 回调
use VXM\Async\AsyncFacade as Async; // run with anonymous function: Async::run(function() { // Do a thing }); // run with class@method Async::run('Your\AsyncJobs\Class@handle'); // call default `handle` method if method not set. Async::run('Your\AsyncJobs\Class');
您可以在一次操作中运行多个作业,并等待所有作业完成。
use VXM\Async\AsyncFacade as Async; Async::run('Your\AsyncJobs\Class@jobA'); Async::run('Your\AsyncJobs\Class@jobB'); Async::run('Your\AsyncJobs\Class@jobC'); Async::run('Your\AsyncJobs\Class@jobD'); // Another way: Async::batchRun( 'Your\AsyncJobs\Class@jobA', 'Your\AsyncJobs\Class@jobB', 'Your\AsyncJobs\Class@jobC', 'Your\AsyncJobs\Class@jobD' ); $results = Async::wait(); // result return from jobs above
事件监听器
在创建异步进程时,您可以添加以下事件钩子
use VXM\Async\AsyncFacade as Async; Async::run(function () { return 123; }, [ 'success' => function ($output) { // `$output` of job in this case is `123`. }, 'timeout' => function () { // A job took too long to finish. }, 'error' => function (\Throwable $exception) { // When an exception is thrown from job, it's caught and passed here. }, ]); // Another way: Async::run('AsyncJobClass@handleMethod', [ 'success' => 'AsyncJobEventListener@handleSuccess', 'timeout' => 'AsyncJobEventListener@handleTimeout', 'error' => 'AsyncJobEventListener@handleError' ]); Async::batchRun( ['AsyncJobClassA@handleMethod', ['success' => 'AsyncJobEventListenerA@handleSuccess']], ['AsyncJobClassB@handleMethod', ['success' => 'AsyncJobEventListenerB@handleSuccess']], ['AsyncJobClassC@handleMethod', ['success' => 'AsyncJobEventListenerC@handleSuccess']] );
处理复杂作业
当处理复杂作业时,您可能需要在它运行之前进行更多设置(例如:作业依赖于 Eloquent 模型)。此包提供您一个 Artisan 命令 make:async-job
以生成作业模板。默认情况下,您的应用程序的所有异步作业都存储在 app/AsyncJobs
目录中。如果 app/AsyncJobs
目录不存在,它将被创建。您可以使用 Artisan CLI 生成新的异步作业
php artisan make:async-job MyJob
创建后,您需要准备您的作业结构,例如
namespace App\AsyncJobs; use App\MyModel; use VXM\Async\Invocation; use App\MyHandleDependency; use Illuminate\Queue\SerializesModels; class MyJob { use Invocation; use SerializesModels; protected $model; /** * Create a new job instance. * * @return void */ public function __construct(MyModel $model) { $this->model = $model; } /** * Execute the job. * * @return void */ public function handle(MyHandleDependency $dependency) { // } }
在此示例中,请注意我们能够直接将 Eloquent 模型传递到异步作业的构造函数中。这是因为作业使用了 SerializesModels
特性,当作业处理时,Eloquent 模型将被优雅地序列化和反序列化。如果您的异步作业在其构造函数中接受 Eloquent 模型,则只有模型的标识符将被序列化到队列中。当作业实际处理时,系统将自动从数据库中重新检索完整的模型实例。这对您的应用程序是完全透明的,并防止了从序列化完整的 Eloquent 模型实例引发的问题。
当作业在异步进程中处理时将调用 handle
方法。请注意,我们能够在作业的 handle
方法上设置类型提示依赖项。Laravel 服务容器自动注入这些依赖项。
如果您想完全控制容器如何将依赖项注入到 handle
方法中,您可以使用容器的 bindMethod
方法。该 bindMethod
方法接受一个回调,该回调接收作业和容器。在回调中,您可以自由调用 handle
方法。通常,您应该从服务提供者调用此方法
use App\AsyncJobs\MyJob; use App\MyHandleDependency; $this->app->bindMethod(MyJob::class.'@handle', function ($job, $app) { return $job->handle($app->make(MyHandleDependency::class)); });
现在异步运行它
use VXM\Async\AsyncFacade as Async; use App\MyModel; use App\AsyncJobs\MyJob; $model = App\MyModel::find(1); Async::run(new MyJob($model)); // or batch run with multiple models: $model2 = App\MyModel::find(2); Async::batchRun( new MyJob($model), new MyJob($model2) );
与队列比较
您可能觉得这个包看起来像队列,并想知道为什么不使用队列呢?
队列是常见异步作业的好选择。此包用于用户在单个请求中需要获取响应的情况,但它是一些需要使用多个进程进行计算或 I/O 重操作的重型任务。而且不需要运行队列监听器。