Laravel のキュー サービスは、さまざまなバックグラウンド キュー サービスに統合された API を提供します。キューを使用すると、電子メールの送信など、時間のかかるタスクの実行を遅らせることができます。これにより、リクエストの応答時間を効果的に短縮できます。
キュー設定ファイルは config/queue.php に保存されます。このファイルには、フレームワークでサポートされているキュー駆動構成の接続の例が含まれています。これらのドライバーには、データベース、Beanstalkd、Amazon SQS、Redis、同期 (ローカル使用) ドライバーが含まれます。
キューに入れられたタスクが使用されないことを示す null という名前のドライバーもあります。
データベース
データベース キュー ドライバーを使用する場合は、キュー タスクを処理するデータ テーブルを追加する必要があります。 queue:table Artisan コマンドを使用して移行テーブルを生成できます。移行テーブルが生成されたら、移行コマンドを使用してデータベースに移行できます。
php artisan queue:tablephp artisan migrate
その他のキューの依存関係
その他のキュー ドライバーは以下にリストされています。対応する依存関係:
デフォルトでは、キューに入れることができるすべてのタスクは app/Jobs ディレクトリに保存されます。 Artisan コマンドによる新しいキュー タスク:
php artisan make:job SendReminderEmail
このコマンドは、app/Jobs ディレクトリに新しいクラスを生成します。このクラスは IlluminateContractsQueueShouldQueue インターフェイスを実装します。これは、laravel がタスクを同期的に実行するのではなく、バックグラウンド タスク キューに追加する必要があることを示します。
タスク クラスは非常に単純で、通常、キュー タスクの実行時に呼び出されるハンドル メソッドのみが含まれます。簡単なタスクの例を見てみましょう:
<?phpnamespace App\Jobs;use App\User;use App\Jobs\Job;use Illuminate\Contracts\Mail\Mailer;use Illuminate\Queue\SerializesModels;use Illuminate\Queue\InteractsWithQueue;use Illuminate\Contracts\Queus\ShouldQueue;class SendReminderEmail extends Job implements ShouldQueue{ use InteractsWithQueue, SerializesModels; protected $user; /** * Create a new job instance. * * @param User $user * @return void */ public function __construct(User $user) { $this->user = $user; } /** * Execute the job. * * @param Mailer $mailer * @return void */ public function handle(Mailer $mailer) { $mailer->send('emails.reminder', ['user' => $this->user], function () { // }); $this->user->reminders()->create(...); }}
この例では、キュー タスクのコンストラクターで Eloquent モデルを直接渡すことができることに注意する必要があります。 SerializesModels トレイトを導入したため、Eloquent モデルはキュー タスクの実行時に正常にシリアル化および逆シリアル化されます。キュー タスクがコンストラクターで Eloquent モデルを受け取った場合、キュー タスクはモデルの ID のみをシリアル化します。タスクを処理する必要がある場合、キュー システムは ID に基づいてデータベースからモデル インスタンスを自動的に取得します。これはアプリケーションに対して完全に透過的であるため、キュー内の完全なモデルをシリアル化するときに発生する可能性のある問題を回避できます。
キュータスクの実行時にハンドルメソッドが呼び出されます。知っておく必要があるのは、タイプ ヒントを使用してタスクのハンドル メソッドに依存関係を挿入できるということです。 Laravel のサービス コンテナは、これらの依存関係を自動的に挿入します。
例外が発生します
タスク中に例外がスローされた場合。タスクを自動的に解放し、キューの最後に追加して、タスクを再試行できるようにします。試行回数が設定された最大回数を超えない限り、タスクは試行のために解放され続けます。最大試行回数を設定するには、 --tries オプションをキュー リスナー queue:listen または queue:work アーティザン タスク コマンドに追加します。キュー リスナーについては後続の章で詳しく紹介します。
タスクをキューの最後まで手動で解放します
release メソッドを使用して、タスクを手動で解放できます。 InteractsWithQueue トレイトは、laravel コマンドによって生成されたタスク クラスに導入されました。このトレイトは、タスク キューにアクセスするための解放メソッドを提供します。このメソッドは 1 つのパラメータを受け入れます: タスクを再度利用可能にするまでの間隔 (秒単位):
public function handle(Mailer $mailer){ if (condition) { $this->release(10); }}
タスクが試行された回数を確認します
上で説明したように、はい、タスクの進行中に例外が発生した場合、キューは自動的にタスクを解放し、再試行できるようにタスクをキューの最後にプッシュします。タスクのリリース時間を確認するには、attempts メソッドを使用します。
public function handle(Mailer $mailer){ if ($this->attempts() > 3) { // }}
app/Http/Controllers/Controller.php にある laravel ベース コントローラーDispatchesJobs トレイトを使用します。この特性は、タスクをキューに簡単にプッシュできるメソッドを提供します。たとえば、ディスパッチメソッド:
<?phpnamespace App\Http\Controllers;use App\User;use Illuminate\Http\Request;use App\Jobs\SendReminderEmail;use App\Http\Controllers\Controller;class UserController extends Controller{ /** * Send a reminder e-mail to a given user. * * @param Request $request * @param int $id * @return Response */ public function sendReminderEmail(Request $request, $id) { $user = User::findOrFail($id); $this->dispatch(new SendReminderEmail($user)); }}
DispatchesJobs trait
もちろん、ルーター内だけでなく、アプリケーション内の任意の場所にタスクを分散したい場合もあります。またはコントローラー。このため、アプリケーションでディスパッチ タスクを呼び出す必要があるクラスで DispatchesJobs トレイトを使用すると、このクラスでディスパッチ メソッドを使用できるようになります。このトレイトの使用の簡単な例を次に示します。
<?phpnamespace App;use Illuminate\Foundation\Bus\DispatchesJobs;class ExampleClass{ use DispatchesJobs;}
ディスパッチ メソッド
代わりに、ディスパッチ グローバル ヘルパー メソッドを使用することもできます:
Route::get('/job', function () { dispatch(new App\Jobs\PerformTask); return 'Done!';});
タスクのキューを指定します
タスクを割り当てる必要があるキューを指定することもできます。
你可以对你的队列任务进行分类,来将任务推送到不同的队列中,你甚至是可以分配各种队列工作的优先权。这并不是推送任务到队列配置文件中所定义的不同的队列连接上,而是仅在单个连接中指定的队列进行的操作。你可以使用任务实例的 onQueue 方法来指定队列。onQueue 来自 Illuminate\Bus\Queueable trait,该性状已经被 App\Jobs\Job 基类所引入:
<?phpnamespace App\Http\Controllers;use App\User;use Illuminate\Http\Request;use App\Jobs\SendReminderEmail;use App\Http\Controllers\Controller;class UserController extends Controller{ /** * Send a reminder e-mail to a given user. * * @param Request $request * @param int $id * @return Response */ public function sendReminderEmail(Request $request, $id) { $user = User::findOrFail($id); $job = (new SendReminderEmail($user))->onQueue('emails'); $this->dispatch($job); }}
有时候,你可能会想要延迟执行队列任务。比如,你可能希望有一个队列任务可以在用户注册后的 5 分钟发送一封提醒邮件。你可以在任务类中使用 delay 方法来完成,该方法是通过 Illuminate\Bus\Queueable 性状提供的:
<?phpnamespace App\Http\Controllers;use App\User;use Illuminate\Http\Request;use App\Jobs\SendReminderEmail;use App\Http\Controllers\Controller;class UserController extends Controller{ /** * Send a reminder e-mail to a given user. * * @param Request $request * @param int $id * @return Response */ public function sendReminderEmail(Request $request, $id) { $user = User::findOrFail($id); $job = (new SendReminderEamil($user))->delay(60 * 5); $this->dispatch($job); }}
在这个例子中,我们指定了任务应该在队列中延迟 5 分钟执行。
注意: Amazon SQS 服务有最大延迟执行限制,其最多可延迟 15 分钟。
任务生命周期事件
你可以使用 Queue::before 和 Queue::after 方法来注册一个回调在队列任务开始之前或者队列执行成功之后调用。回调为添加额外的日志,持续执行子任务或者增加统计信息等提供了非常好的机会。比如,我们可以在 laravel 的 AppServiceProvider 中定义一个任务成功执行后的事件监听,并附加一个回调到事件中:
<?phpnamespace App\Providers;use Queue;use Illuminate\Support\ServiceProvider;use Illuminate\Queue\Events\JobProcessed;class AppServiceProvider extends ServiceProvider{ /** * Bootstrap any application services. * * @return void */ public function boot() { Queue::after(function (JobProcessed $event) { // $event->connectionName // $event->job // $event->data }); } /** * Register the service provider. * * @return void */ public function register() { // }}
开始进行队列监听
laravel 包含了一个 Artisan 命令来运行推送到队列中的任务的执行。你可以使用 queue:listen 命令来运行监听器:
php artisan queue:listen
你也可以指定监听哪一个连接的队列:
php artisan queue:listen connection-name
你需要注意的是,这个命令执行,它会持续的运行,除非你手动的进行停止。你可以通过使用 Supervisor 进程监控来确保队列监听器的运行。
队列优先级
你可以通过使用 , 来分割连接的队列,以确定队列的运行优先级:
php artisan queue:listen --queue=high,low
在这个例子中,任务会优先执行 high 的队列,然后才会运行 low 队列中的任务。
指定任务的超时时间
你可以设置任务的超时时间:
php artisan queue:listen --timeout=60
指定队列的睡眠时间
另外,你可以指定队列轮询新的任务需要等待的时间(秒):
php artisan queue:listen --sleep=5
你需要注意的是,队列只会在队列中没有需要执行的任务时才会进行睡眠。如果队列中有多个可执行的任务,那么队列会持续的进行任务的执行,而不会进行睡眠操作。
执行队列的首个任务
你可以使用 queue:work 命令来只执行队列的首个任务:
php artisan queue:work
Supervisor 是 Linux 操作系统的一个进程监控器,并且它可以自动的在 queue:listen 或者 queue:work 命令失败时进行重启。你可以使用下面的命令在 Ubuntu 中安装 Supervisor:
sudo apt-get install supervisor
Supervisor 配置文件通常都存储在 /etc/supervisor/conf.d 目录中。在这个目录中,你可以创建任意数量的配置文件来指导 supervisor 来管理监控进程。比如,让我们创建一个 laravel-worker.conf 文件来开始和监控 queue:work 进程:
[program:laravel-worker]process_name=%(program_name)s_%(process_num)02dcommand= php /home/forge/app.com/artisan queue:work sqs --sleep=3 --tries=3 --daemonautostart=trueautorestart=trueuser=forgenumprocs=8redirect_stderr=truestdout_logfile=/home/forge/app.com/worker.log
上面的例子中,numprocs 会指导 Supervisor 运行 8 个 queue:work 进程并且对其进行监控,它会自动的在其失败时进行重启。当然,你可以修改命令的 queue:work sqs 部分来使用你所期望的队列驱动。
一旦配置文件创建完成,你可以使用下面的命令来更新 Supervisor 的配置文件和启动进程:
sudo supervisorctl rereadsudo supervisorctl updatesudo supervisorctl start laravel-worker:*
关于更多的 Supervisor 的配置信息,请参考 Supervisor documentation。另外,你也可以使用 Laravel Forge 来从一个方便的 Web 界面自动的配置和管理你的 Supervisor 配置。
queue:work Artisan 命令包含了一个 --daemon 选项来强迫队列工作持续的执行任务而不重新引导框架。这相比较 queue:lsten 命令来说会显著的减少 CPU 的消耗:
php artisan queue:work connection-name --daemonphp artisan queue:work connection-name --daemon --sleep=3php artisan queue:work connection-name --daemon --sleep=3 --tries=3
就如你所看到的,queue:work 任务支持和 queue:listen 差不多的选项。你可以通过使用 php artisan help queue:work 命令来显示可用选项。
对于守护进程的编码注意事项
队列任务的守护进程不会在每个任务执行之前重新引导框架,所以,你应该注意在你的任务完成时清除一些比较重的资源消耗。比如,如果你使用 GD 类库来处理图片,你应该在任务执行完成后使用 imagedestroy 来将其从内存中进行释放。
因为队列工作的守护进程是一个常驻进程。它不会再你的代码改变时进行重启。所以,你应该使用部署脚本来在代码变更时重新部署使用守护进程的队列工作:
php artisan queue:restart
该命令会优雅的指导队列完成当前的任务后死亡,所以不会存在任务的遗漏。你应该注意的是,当你执行 queue:restart 命令时,队列工作就会死亡,所以你应该使用一种进程管理器,比如 Supervisor,你可以配置它来自动的重启队列工作。
注意:该命令依赖于缓存系统来制定重启时间表。默认的 APCu 不支持在 CLI 中运行任务。如果你使用 APCu,你应该添加 apc.enable_cli=1 到你的 APCu 配置中。
由于很多事情并不能如计划中的那样进行,有时候队列任务的执行可能会失败,不要担心,它发生对我们来说是最好的!laravel 包含了一种便捷的方式来指定任务应该重复尝试的次数。如果任务被重复执行到指定的次数,它就会被记录到 failed_jobs 表中。这个表的名字你可以通过 config/queue.php 配置文件进行定制。
你可以使用 queue:failed-table 命令来生成一个 failed_jobs 迁移表:
php artisan queue:failed-table
当你运行队列监听器时,你可以使用 --tries 选项来指定任务的最大尝试次数:
php artisan queue:listen connection-name --tries=3
如果你希望在队列任务失败时执行某些操作,你可以使用 Queue::failing 方法来注册一个监听事件。这个事件对通过 email 或者 HipChat 通知你的团队提供了一个很好的机会。比如,你可以在 AppServiceProvider 在事件中附加一个回调:
<?phpnamespace App\Providers;use Queue;use Illuminate\Queue\Events\JobFailed;use Illuminate\Support\ServiceProvider;class AppServiceProvider extends ServiceProvider{ /** * Bootstrap any application service. * * @return void */ public function boot() { Queue::failing(function (JobFailed $event) { // $event-connectionName // $event->job // $event-data }); } /** * Register the service provider. * * @return void */ public function register() { // }}
任务类中的失败方法
你可以在任务类中定义一个 failed 方法来进行更为精细的控制,这允许你在任务出现失败时来执行一些指定的动作:
<?phpnamespace App\Jobs;use App\Jobs\Job;use Illuminate\Contracts\Mail\Mailer;use Illuminate\Queue\SerializesModels;use Illuminate\Queue\InteractsWithQueue;use Illuminate\Contracts\Queue\ShouldQueue;class SendReminderEmail extends Job implements ShouldQueue{ use InteractsWithQueue, SerializesModels; /** * Execute the job. * * @param Mailer $mailer * @return void */ public function handle(Mailer $mailer) { // } /** * Handle a job failure. * * @return void */ public function failed() { // Called when the job is failing... }}
你可以使用 queue:failed Artisan 命令来显示数据库 failed_jobs 表中失败的任务:
php artisan queue:failed
queue:failed 命令会列出任务的 ID,连接,队列,和失败时间。任务 ID 可以用来进行失败任务的尝试。比如,你可以尝试重新执行 ID 为 5 的任务:
php artisan queue:retry 5
你可以使用 queue:retry all 命令来重启所有的任务:
php artisan queue:retry all
如果你希望删除失败的任务,你可以使用 queue:forget 命令:
php artisan queue:forget 5
你可以使用 queue:flush 命令来清除所有失败的任务:
php artisan queue:flush