Laravel5.2中的Queue -- 队列

1.简介

Laravel 队列服务为各种不同的后台队列提供了统一的API。队列允许你推迟耗时任务(例如发送邮件)的执行,从而大幅提高web请求速度。

配置

队列配置文件存放在config/queue.php。在该文件中你将会找到框架自带的每一个队列驱动的连接配置,包括数据库、Beanstalkd、 IronMQ、 Amazon SQS、 Redis以及同步(本地使用)驱动。其中还包含了一个null队列驱动以拒绝队列任务。

database

为了使用database队列驱动,需要一张数据库表来存放任务,要生成创建该表的迁移,运行Artisan命令queue:table,迁移被创建好了之后,使用migrate命令运行迁移:

1
2
php artisan queue:table
php artisan migrate

其它队列依赖

  • Amazon SQS: aws/aws-sdk-php ~3.0
  • Beanstalkd: pda/pheanstalk ~3.0
  • Redis: predis/predis ~1.0

2.生成Job类

2.1Job任务类都存在app/Jobs目录中, 可以使用命令行生成命令

1
php artisan make:job SendEmail

该命令将会在app/Jobs目录下生成一个新的类,并且该类实现了Illuminate\Contracts\Queue\ShouldQueue接口,告诉Laravel该任务应该被推送到队列而不是同步运行。

2.2 SendEmail文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php

namespace App\Jobs;

use App\Jobs\Job;
use App\User;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendEmail extends Job implements ShouldQueue {
use InteractsWithQueue, SerializesModels;

protected $msg;

/**
* 创建一个新的任务实例
*
* @param User $user
*/

public function __construct($msg) {
$this->msg = $msg;
}

/**
* 执行任务
*
* @return void
*/

public function handle() {

sleep(2);
\Log::info('Queue handle:' . $this->msg);
$this->delete();

}
}

注:我们能够直接将Eloquent模型传递到对列任务的构造函数中。由于该任务使用了SerializesModels trait,Eloquent模型将会在任务被执行是优雅地序列化和反序列化。如果你的队列任务在构造函数中接收Eloquent模型,只有模型的主键会被序列化到队列,当任务真正被执行的时候,队列系统会自动从数据库中获取整个模型实例。这对应用而言是完全透明的,从而避免序列化整个Eloquent模型实例引起的问题。
handle方法在任务被队列处理的时候被调用,注意我们可以在任务的handle方法中进行依赖注入。Laravel服务容器会自动注入这些依赖。

2.3手动释放任务

如果你想要手动释放任务,生成的任务类中自带的InteractsWithQueue trait提供了释放队列任务的release方法,该方法接收一个参数——同一个任务两次运行之间的等待时间:

1
2
3
4
5
public function handle(Mailer $mailer){
if (condition) {
$this->release(10);
}
}

2.4检查尝试运行次数

正如上面提到的,如果在任务处理期间发生异常,任务会自动释放回队列中,你可以通过attempts方法来检查该任务已经尝试运行次数:

1
2
3
4
5
public function handle(Mailer $mailer){
if ($this->attempts() > 3) {
//
}
}

3 推送任务到队列

1
2
3
4
5
6
7
8
9
10
11
Route::get('/', function () {

for ($i = 0; $i < 10; $i++) {
$msg = uniqid($i);
dispatch(new App\Jobs\SendReminderEmail($msg));
\Log::info('Queue pushed:'.$msg);
}

return 'Done!';

});

3.1 DispatchesJobs Trait

当然,有时候你想要从应用中路由或控制器之外的某些地方分发任务,因为这个原因,你可以在应用的任何类中包含DispatchesJobs trait,从而获取对分发方法的访问,举个例子,下面是使用该trait的示例类:

1
2
3
4
5
6
7
8
9
<?php

namespace App;

use Illuminate\Foundation\Bus\DispatchesJobs;

class ExampleClass{
use DispatchesJobs;
}

3.2 dispatch方法

或者,你也可以使用全局的dispatch方法:

1
2
3
4
Route::get('/job', function () {
dispatch(new App\Jobs\PerformTask);
return 'Done!';
});

3.3 为任务指定队列

1
2
3
4
5
6
7
8
9
10
11
Route::get('/', function () {

for ($i = 0; $i < 10; $i++) {
$msg = uniqid($i);
dispatch((new App\Jobs\SendReminderEmail($msg))->onQueue('emails'));
\Log::info('Queue pushed:'.$msg);
}

return 'Done!';

});

3.4 延迟任务

有时候你可能想要延迟队列任务的执行。例如,你可能想要将一个注册15分钟后给消费者发送提醒邮件的任务放到队列中,可以通过使用任务类上的delay方法来实现,该方法由Illuminate\Bus\Queueable trait提供:

1
2
3
4
5
6
7
8
9
10
11
Route::get('/', function () {

for ($i = 0; $i < 10; $i++) {
$msg = uniqid($i);
dispatch((new App\Jobs\SendReminderEmail($msg))->delay(60));//我们指定任务在队列中开始执行前延迟60秒。
\Log::info('Queue pushed:'.$msg);
}

return 'Done!';

});

3.5 任务事件

Queue::after 方法允许你在队列任务执行成功后注册一个要执行的回调函数。在该回调中我们可以添加日志、统计数据。例如,我们可以在Laravel内置的 AppServiceProvider 中添加事件回调:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php

namespace App\Providers;

use Queue;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{

/**
* Bootstrap any application services.
*
* @return void
*/

public function boot()
{

Queue::after(function ($connection, $job, $data) {
//
});
}

/**
* Register the service provider.
*
* @return void
*/

public function register()
{

//
}
}

4 运行队列监听器

使用 queue:listen 命令运行监听器:

1
php artisan queue:listen

还可以指定监听器使用哪个队列连接:

1
php artisan queue:listen connection

注:一旦任务开始后,将会持续运行直到手动停止。你可以使用一个过程监视器如Supervisor来确保队列监听器没有停止运行。

4.1 队列优先级

你可以传递逗号分隔的队列连接列表到listen任务来设置队列优先级:

1
php artisan queue:listen --queue=high,low

在本例中,high队列上的任务总是在从low队列移动任务之前被处理。

4.2 指定任务超时参数

你还可以设置每个任务允许运行的最大时间(以秒为单位):

1
php artisan queue:listen --timeout=60

4.3 指定队列睡眠时间

此外,可以指定轮询新任务之前的等待时间(以秒为单位):

1
php artisan queue:listen --sleep=5

注:需要注意的是队列只会在队列上没有任务时“睡眠”,如果存在多个有效任务,该队列会持续运行,从不睡眠。