Laravel框架中队列和工作(Queues、Jobs)操作实例详解


Posted in PHP onApril 06, 2020

在我们的web应用中,经常会遇到这样的情况:

用户在进行了某项操作后,我们需要在后台完成一个耗时且耗费资源的任务,以对应用户的操作。

通常来说,web应用中的操作都是同步的(synchronous),即用户的操作可以立即得到回馈。

但是在以上情况下,同步等待操作结果将是灾难性的。比如用户点击了申请密码重置邮件,倘若我们让用户一直停滞在等待页面,直至邮件发送成功,那么用户体验将非常地不好,因为有时候可能需要很长的时间才能将邮件发送完成。

从另一个角度来说,如果我们服务器处于高负荷的情况,当多个用户同时请求发送邮件等操作时,我们不希望同时地给服务器增加负荷,否则可能会导致服务器崩溃,造成无法预估的情况。

从以上的讨论可以看出,我们需要一种机制,可以非同步地响应用户操作,并且不会给服务器增加过大的负荷。

那么这样一种机制就是Queues和Jobs(即队列和工作)。

如果你系统地学习过计算机科学,那么队列的概念你应该不陌生。假设我们去银行办事,我们拿了一个号,发现前面有8个人在等待,那么我们实际上就处在一个队列之中,队列中靠前的人会先被叫到号码,并且叫号的顺序即拿号的顺序。这样的队列就叫做Queue,采用的是先到先处理的方式,不允许插队的情况存在。而我们要办的事情就叫Job。

在Laravel中,我们可以很方便地使用Queues及Jobs来达到我们的目的。首先我们需要先来看一下,Laravel中有哪些Queues。

打开config/queue.php,我们可以看到几种常见的队列设置:

return [      
  
  /*      
  |--------------------------------------------------------------------------      
  | Default Queue Connection Name      
  |--------------------------------------------------------------------------      
  |      
  | Laravel's queue API supports an assortment of back-ends via a single      
  | API, giving you convenient access to each back-end using the same      
  | syntax for every one. Here you may define a default connection.      
  |      
  */      
  
  'default' => env('QUEUE_DRIVER', 'sync'),      
  
  /*      
  |--------------------------------------------------------------------------      
  | Queue Connections      
  |--------------------------------------------------------------------------      
  |      
  | Here you may configure the connection information for each server that      
  | is used by your application. A default configuration has been added      
  | for each back-end shipped with Laravel. You are free to add more.      
  |      
  | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"      
  |      
  */      
  
  'connections' => [      
  
    'sync' => [      
      'driver' => 'sync',      
    ],      
  
    'database' => [      
      'driver' => 'database',      
      'table' => 'jobs',      
      'queue' => 'default',      
      'retry_after' => 90,      
    ],      
  
    'beanstalkd' => [      
      'driver' => 'beanstalkd',      
      'host' => 'localhost',      
      'queue' => 'default',      
      'retry_after' => 90,      
    ],      
  
    'sqs' => [      
      'driver' => 'sqs',      
      'key' => env('SQS_KEY', 'your-public-key'),      
      'secret' => env('SQS_SECRET', 'your-secret-key'),      
      'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),      
      'queue' => env('SQS_QUEUE', 'your-queue-name'),      
      'region' => env('SQS_REGION', 'us-east-1'),      
    ],      
  
    'redis' => [      
      'driver' => 'redis',      
      'connection' => 'default',      
      'queue' => 'default',      
      'retry_after' => 90,      
      'block_for' => null,      
    ],      
  
  ],      
  
  /*      
  |--------------------------------------------------------------------------      
  | Failed Queue Jobs      
  |--------------------------------------------------------------------------      
  |      
  | These options configure the behavior of failed queue job logging so you      
  | can control which database and table are used to store the jobs that      
  | have failed. You may change them to any database / table you wish.      
  |      
  */      
  
  'failed' => [      
    'database' => env('DB_CONNECTION', 'mysql'),      
    'table' => 'failed_jobs',      
  ],      
  
];

在connections中,我们看到sync这个连接。sync是Laravel默认的队列,代表的就是synchronous,即同步队列。

今天我们要来看一下,如何使用database,即数据库来实现异步任务处理。

要使用database来作为队列的内部实现机制,我们需要建立一张用于储存Jobs的表:

$ php artisan queue:table     
$ php artisan migrate

以上命令将会在数据库创建名为jobs的表。

队列我们有了,那么现在我们来看一下Jobs。Laravel中jobs文件默认位置在app/Jobs文件夹下,我们可以通过make:job这个Artisan命令快速创建我们的job类:

$ php artisan make:job SendEmail

生成的job会实现Illuminate\Contracts\Queue\ShouldQueue这个接口,表明生成的job对象将被推到队列中进行异步处理。

job类其实很简单,里面只有一个名为handle的方法,该方法在job被queue处理的时候自动被调用。

在上面的命令中,我们创建了一个名为SendEmail的类:

<?php    
  
namespace App\Jobs;    
  
use App\Email;    
use Illuminate\Bus\Queueable;    
use Illuminate\Queue\SerializesModels;    
use Illuminate\Queue\InteractsWithQueue;    
use Illuminate\Contracts\Queue\ShouldQueue;    
use Illuminate\Foundation\Bus\Dispatchable;    
  
class SendEmail implements ShouldQueue    
{    
  use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;    
  
  protected $email;    
  
  /**    
   * Create a new job instance.    
   *    
   * @param Podcast $podcast    
   * @return void    
   */    
  public function __construct(Email $email)    
  {    
    $this->email = $email;    
  }    
  
  /**    
   * Execute the job.    
   *    
   * @param AudioProcessor $processor    
   * @return void    
   */    
  public function handle()    
  {    
    // Process email and send the email to recipient(s)    
    // 这里我们可以处理我们的邮件并将邮件发送至接收人 
  }    
}

可以看到,我们可以将model传递进job的constructor中。Laravel会自动序列化(Serialize)模型的识别信息,在job真正被处理的时候,完整的模型数据才会被从数据库调用出来。另外,在handle方法中,我们也可以注入我们的依赖dependencies。

好了,现在我们有了job类,可以创建job对象了,那么如何把job添加进队列呢?

在我们的控制器中,我们可以调用job的dispatch方法来将其添加进队列中:

<?php  
  
namespace App\Http\Controllers;  
  
use App\Jobs\SendEmail;  
use Illuminate\Http\Request;  
use App\Http\Controllers\Controller;  
use App\Email; 
  
class EmailsController extends Controller  
{  
  /**  
   * Store a new email.  
   *  
   * @param Request $request  
   * @return Response  
   */  
  public function send(Request $request)  
  {  
    // Create email...  
    // 这里我们提取email信息并创建$email, Email是我们自定义的Model 
    $email = Email::create($request->only('sender', 'to', 'content')); 
  
    SendEmail::dispatch($email);  
  }  
}

这样一来,每当我们的控制器调用send方法时,就会创建一个SendEmail的job在数据库中。

那么怎么样调用Queue Worker来处理我们的jobs呢?

在.env文件中,我们将QUEUE_DRIVER=sync改为QUEUE_DRIVER=database。

接下来,我们运行以下Artisan命令:

$ php artisan queue:work

队列的worker会一直运行,每当有任务被添加进数据库jobs表中,worker便会自动抓取出任务进行处理。当任务失败时,worker会重复执行任务,直至最大尝试次数(默认为255)。我们可以手动设置最大尝试次数:

$ php artisan queue:work --tries=3

当然,我们也可以手动设置任务的超时(默认90s,在config/queue.php中的retry_after设置):

$ php artisan queue:work --timeout=30

最后,当没有任务的时候,我们可以设置一个睡眠时间,当worker在睡眠时间时,将不会处理任务:

$ php artisan queue:work --sleep=10

上面的命令意思是每当worker处理完所有任务后,会睡眠10s,然后才会再次检查任务队列

本文使用Laravel 5.6进行讲解

本文主要讲解了Laravel框架中队列和工作(Queues、Jobs)操作实例详解,更多关于Laravel框架的使用技巧请查看下面的相关链接

PHP 相关文章推荐
使用php重新实现PHP脚本引擎内置函数
Mar 06 PHP
php的$_FILES的临时储存文件与回收机制实测过程
Jul 12 PHP
wordpress自定义url参数实现路由功能的代码示例
Nov 28 PHP
简单的php缓存类分享     php缓存机制
Jan 22 PHP
兼容PHP和Java的des加密解密代码分享
Jun 26 PHP
ThinkPHP查询返回简单字段数组的方法
Aug 25 PHP
PHP实现生成唯一会员卡号
Aug 24 PHP
yii2 页面底部加载css和js的技巧
Apr 21 PHP
PHP简单获取网站百度搜索和搜狗搜索收录量的方法
Aug 23 PHP
php cookie 详解使用实例
Nov 03 PHP
PHP实现财务审核通过后返现金额到客户的功能
Jul 04 PHP
PHP开发api接口安全验证操作实例详解
Mar 26 PHP
Laravel实现批量更新多条数据
Apr 06 #PHP
PHP正则之正向预查与反向预查讲解与实例
Apr 06 #PHP
TP5框架安全机制实例分析
Apr 05 #PHP
TP5框架实现自定义分页样式的方法示例
Apr 05 #PHP
TP5框架model常见操作示例小结【增删改查、聚合、时间戳、软删除等】
Apr 05 #PHP
TP5框架实现签到功能的方法分析
Apr 05 #PHP
TP5框架页面跳转样式操作示例
Apr 05 #PHP
You might like
php ajax 静态分页过程形式
2011/09/02 PHP
php的POSIX 函数以及进程测试的深入分析
2013/06/03 PHP
PHP中让curl支持sock5的代码实例
2015/01/21 PHP
PHP生成图片验证码功能示例
2017/01/12 PHP
在laravel中实现ORM模型使用第二个数据库设置
2019/10/24 PHP
JS基础之undefined与null的区别分析
2011/08/08 Javascript
js 浏览器事件介绍
2012/03/30 Javascript
IE6下拉框图层问题探讨及解决
2014/01/03 Javascript
js Object2String方便查看js对象内容
2014/11/24 Javascript
node.js中的path.resolve方法使用说明
2014/12/08 Javascript
分享十五款 jQuery 社交网络分享插件
2015/05/16 Javascript
jquery自定义插件——window的实现【示例代码】
2016/05/06 Javascript
基于JS实现回到页面顶部的五种写法(从实现到增强)
2016/09/03 Javascript
AngularJS中过滤器的使用与自定义实例代码
2016/09/17 Javascript
jQuery实现滚动条滚动到子元素位置(方便定位)
2017/01/08 Javascript
js断点调试经验分享
2017/12/08 Javascript
基于vue-upload-component封装一个图片上传组件的示例
2018/10/16 Javascript
Vue开发之封装分页组件与使用示例
2019/04/25 Javascript
vue+elementUI实现图片上传功能
2019/08/20 Javascript
详解vue中v-model和v-bind绑定数据的异同
2020/08/10 Javascript
[01:15:16]DOTA2-DPC中国联赛 正赛 Elephant vs Aster BO3 第一场 1月26日
2021/03/11 DOTA
Python 类的继承实例详解
2017/03/25 Python
python查询mysql,返回json的实例
2018/03/26 Python
Python 从相对路径下import的方法
2018/12/04 Python
Python:Numpy 求平均向量的实例
2019/06/29 Python
如何实现Django Rest framework版本控制
2019/07/25 Python
深入浅析Python 函数注解与匿名函数
2020/02/24 Python
Python telnet登陆功能实现代码
2020/04/16 Python
pandas 像SQL一样使用WHERE IN查询条件说明
2020/06/05 Python
OpenCV4.1.0+VS2017环境配置的方法步骤
2020/07/09 Python
请说出几个常用的异常类
2013/01/08 面试题
室内设计实习自我鉴定
2013/09/25 职场文书
2015年机关党委工作总结
2015/05/23 职场文书
酒店开业主持词
2015/07/02 职场文书
OpenCV-Python使用cv2实现傅里叶变换
2021/06/09 Python
Go语言设计模式之结构型模式
2021/06/22 Golang