为什么使用队列
在Web开发中,我们经常会遇到需要批量处理任务的场景,比如群发邮件、秒杀资格获取等,我们将这些耗时或者高并发的操作放到队列中异步执行可以有效缓解系统压力、提高系统响应速度和负载能力。
实现队列有多种方式,Laravel也支持多种队列实现驱动,比如数据库
、Redis
、Beanstalkd
、IronMQ
及Amazon SQS
等,下面以Redis
驱动为例演示在Laravel
如何实现队列创建、推送和执行。
配置
首先我们需要在配置文件中配置默认队列驱动为Redis
,队列配置文件为config/queue.php
,也可以在.env
中修改QUEUE_DRIVER
为redis
。
reids需要配置让我们项目能够连接使用,具体参考laravel5.*安装使用Redis以及解决Class ‘Predis\Client’ not found和Fatal error: Non-static method Redis::set() cannot be called statically错误
编写队列任务
首先我们通过如下Artisan
命令创建任务类:
php artisan make:job RecordLoginfo --queued
--queued
选项表示生成的任务类实现了ShouldQueue
接口,会被推送到队列而不是同步执行。
运行成功后会在app/Jobs
目录下生成一个RecordLoginfo.php
文件,我们修改其内容如下:
<?php
namespace App\Jobs;
use App\Jobs\Job;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Bus\SelfHandling;
use Illuminate\Contracts\Queue\ShouldQueue;
use Log;
class RecordLoginfo extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
protected $id;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($id)
{
$this->id = $id;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
Log::info("Queue:".$this->id);
}
}
上面的操作我们会在日志文件中打印一条日志,同时方便调试;文件位于storage/logs/laravel/log
,编写好任务类之后我们来看如何将任务推送到队列中:
推送队列任务
手动分发任务
我们可以使用控制器中的DispatchesJobs trait
(该trait
在控制器基类Controller.php
中引入)提供的dispatch
方法手动分发任务:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Jobs\RecordLoginfo;
class MailController extends Controller
{
//其他方法
//发送提醒邮件
public function recordLoginfo(Request $request,$id){
$this->dispatch(new RecordLoginfo($id));
}
}
然后在routes.php
中定义路由:
Route::get(‘mail/recordloginfo/{id}’,’MailController@recordLoginfo’);
运行队列监听器
在浏览器中访问http://laravel.app:8000/mail/recordloginfo/1
,此时任务被推送到Redis
队列中,我们还需要在命令行中运行Artisan
命令执行队列中的任务。Laravel为此提供了三种Artisan命令:
- queue:work 默认只执行一次队列请求, 当请求执行完成后就终止;
- queue:listen 监听队列请求,只要运行着,就能一直接受请求,除非手动终止;
queue:work —daemon 同 listen 一样, 只要运行着,就能一直接受请求,不一样的地方是在这个运行模式下,当新的请求到来的时候,不重新加载整个框架,而是直接 fire 动作。能看出来,
queue:work --daemon
是最高级的,一般推荐使用这个来处理队列监听。注:使用
queue:work --daemon
,当更新代码的时候,需要停止,然后重新启动,这样才能把修改的代码应用上。 所以我们接下来在命令行中运行如下命令:php artisan queue:work —daemon
然后去查看邮箱会收到提醒邮件
推送任务到指定队列
上述操作将队列推送到默认队列,即配置文件中的default
,当然你还可以将任务推送到指定队列:
public function RecordLoginfo(Request $request,$id){
$job = (new RecordLoginfo($id))->onQueue('emails');
$this->dispatch($job);
}
延迟任务执行
除此之外,Laravel还支持延迟任务执行时间,这里我们指定延迟1分钟执行任务:
public function RecordLoginfo(Request $request,$id){
$job = (new RecordLoginfo($id))->delay(60);
$this->dispatch($job);
}
从请求中分发任务
此外,我们还可以将HTTP请求实例映射到任务中,然后从请求实例中获取参数填充任务类的构造函数,如果请求中不包含该参数,甚至还可以传递额外参数,这可以通过DispatchesJobs trait
提供的dispatchFrom
方法来实现:
public function RecordLoginfo(Request $request,$id){
$this->dispatchFrom('App\Jobs\RecordLoginfo',$request,['id'=>$id]);
}
当然我们需要对RecordLoginfo
任务类的构造函数做如下修改:
public function __construct($id)
{
$this->id = $id;
}
构造函数中的$id就是从额外参数中获取到的。