某些较耗时的工作像是寄信、发通知等,如果卡在处理请求的过程中的话就会造成使用者要多等上数秒才能收到回应,不过这些工作本身对於回应毫无影响,这时候就能考虑将这些工作放入队列中等待执行,然後马上回应使用者正在寄信或发通知中。
要建立队列的话必须指定存放的地方,像是资料库。
定义队列存放的方式写在 config/queue.php
中。
<?php
return [
'default' => env('QUEUE_CONNECTION', 'sync'),
'connections' => [
//...
],
'failed' => [
//...
],
];
首先看 connections
,这就是定义伫列存放位置的地方。
'connections' => [
'sync' => [
'driver' => 'sync',
],
'database' => [
'driver' => 'database',
'table' => 'jobs',
'queue' => 'default',
'retry_after' => 90,
'after_commit' => false,
],
// ...
],
要注意的是 connection
只是定义存放位置、跟一些执行队列的方法,而各个 connection
中可以有复数个队列,例如我可以在 database
中建立 deafault
队列、 email
队列、 notification
队列,并在执行任务时指定不同的队列。
如果没指定队列的话, Laravel 就会将工作放入各个 connection
定义的 default
队列。
而如果没指定 connection
,就会用 config/queue.php
定义的 default connection
'default' => env('QUEUE_CONNECTION', 'sync'),
跟其他 default
一样有用环境变数,记得在 .env
改。
sync
其实没有队列,就是及时执行工作的意思,适合在开发环境用。
database
需要在资料库中建表单来储存队列工作,预设资料表名称是 jobs
。
可以利用预设的指令来产生建立 jobs
资料表的 migration 。
sail artisan queue:table
sail artisan migrate
至於 failed
定义对於执行时出现错误的工作,该存到什麽地方等待确认。
可以用指令产生预设的 database table。
php artisan queue:failed-table
php artisan migrate
能够被存入队列的物件需要特别定义,可以先用指令产生 Job
类别,预设放在 app/Jobs/
目录。
sail artisan make:job ProcessMail
app/Notifications/VerifyNotification.php
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessMail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct()
{
//
}
public function handle()
{
//
}
}
引用了很多模组,最主要的是 ShouldQueue
介面以及 Queueable
,有这两个属性的话任何类别都能被存入队列等待执行,例如 Notification。
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class VerifyNotification extends Notification implements ShouldQueue
{
use Queueable;
//...
}
可以帮 Notification 加上 ShouldQueue
跟 Queueable
,这样用 notify
时发送通知的工作就会先被存入队列,不会即时执行。
use App\Notifications\VerifyNotification;
$user->notify(new VerifyNotification());
回到我们刚刚建立的 Job ,还有一个重点是 Job 必需要有 handle()
函式,实际被存入队列而後执行的会是 handle()
中的内容。
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
class ProcessMail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $user;
protected $mail;
public function __construct( $user , $mail )
{
$this->user = $user;
$this->mail = $mail;
}
public function handle()
{
Mail::to($this->user)->send($this->mail);
}
}
不过参数是在建立 Job 物件时传入的,所以要在 __construct()
中设定属性。
建好 Job 後就能用 dispatch()
将任务放入队列等待执行。
用 Job 改写寄验证信的功能。
其实 Laravel 原本的验证信功能用的 Notification 只要加上 ShouldQueue
就可以被队列执行了,不过为了讲解来画蛇添足一下。
use App\Jobs\ProcessMail;
class User extends Authenticatable implements MustVerifyEmail
{
//...
public function sendEmailVerificationNotification()
{
ProcessMail::dispatch( $this , new CustomVerifyMail);
}
}
这时候可以试着申请帐号寄验证信,不过可能会发现请求执行很久而解信件马上就被寄出。
可以确认有没有将 .env
中的设定改为 sync
以外的 connection
,我是改成用 database
.env
QUEUE_CONNECTION=database
改完 .env
记得要让服务器重新载入才能够应用。
sail artisan config:cache
再寄一次可以看到工作被存入资料库了。
像前面说的执行工作时没指定的话会把工作排到 default connection 里的 default queue ,要指定的话在 dispatch 时指定:
ProcessPodcast::dispatch($podcast)
->onConnection('sqs')
->onQueue('processing');
不过只是存入队列并不会执行,要用指令开启队列执行功能。
sail artisan queue:work
queue:work
会以目前的程序码进行任务处理,如果改了程序码要先关掉队列 (Ctrl + C
)後再开 queue:work
。
如果想要省着一步的话可以用 queue:listen
,不过执行速度会慢一点。
如果没指定的话 queue:work
会处理的是 default connection 中的 default队列任务,可以在执行 queue:work
时指定要处理哪个 connection。
sail artisan queue:work redis
或进一步指定用处理个队列
php artisan queue:work redis --queue=emails
当工作执行时出现 EXception
且未被 catch 的话就会失败, queue:work
会在失败时试图重复执行,预设执行 3 次,可以在执行时决定重复尝试的次数。
php artisan queue:work redis --tries=3 --backoff=3
--backoff
则是每次尝试要间隔几秒。
另外可以在 Job 类别中定义当工作失败时要执行的 failed()
函式。
class ProcessMail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $user;
protected $mail;
public function __construct( $user , $mail )
{
$this->user = $user;
$this->mail = $mail;
}
public function handle()
{
Mail::to($this->user)->send($this->mail);
}
public function failed(Throwable $exception)
{
// log failure ...
}
}
failed()
会收到 handle()
抛出的 Exception ,除此之外并不会跟 handle()
共用任何物件中的参数,执行 handle()
跟执行 failed()
的物件是分开的。
1293. Shortest Path in a Grid with Obstacles Elimi...
在上篇文章介绍了 Kolla 跟 Kolla-Ansible 部署 OpenStack 的方法。在设...
今天因应工作上的需求 在专案中汇入 .svg 的向量图档 却出现了以下错误画面 这才发现原来这张 ....
前阵子托朋友的福,去了富士山第一排露营。露营的好处就是不用想行程,光是准备吃的东西跟睡觉的地方就够忙...
Azure DevOps上的Repos可以分成Git Repo和TFVC Repo,这篇文章就先简单...