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 XML操作类DOMDocument
Dec 16 PHP
那些年一起学习的PHP(二)
Mar 21 PHP
php5.2以下版本无json_decode函数的解决方法
May 25 PHP
PHP中字符安全过滤函数使用小结
Feb 25 PHP
php调整服务器时间的方法
Apr 03 PHP
PHP json_encode() 函数详解及中文乱码问题
Nov 05 PHP
浅析Yii2中GridView常见操作
Apr 22 PHP
PHP递归获取目录内所有文件的实现方法
Nov 01 PHP
PHP将身份证正反面两张照片合成一张图片的代码
Apr 08 PHP
Laravel框架生命周期与原理分析
Jun 12 PHP
php输出文字乱码的解决方法
Oct 04 PHP
PHP设计模式(三)建造者模式Builder实例详解【创建型】
May 02 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编程网上资源导航
2006/10/09 PHP
使用TinyButStrong模板引擎来做WEB开发
2007/03/16 PHP
Zend Guard一些常见问题解答
2008/09/11 PHP
php中定时计划任务的实现原理
2013/01/08 PHP
分享php代码将360浏览器导出的favdb的sqlite数据库文件转换为html
2015/12/09 PHP
javascript document.images实例
2008/05/27 Javascript
javascript删除option选项的多种方法总结
2013/11/22 Javascript
页面图片浮动左右滑动效果的简单实现案例
2014/02/10 Javascript
基于jquery实现等比缩放图片
2014/12/03 Javascript
JavaScript中的splice方法用法详解
2016/07/20 Javascript
jQuery实现可拖拽3D万花筒旋转特效
2017/01/03 Javascript
Node.js 8 中的重要新特性
2017/06/28 Javascript
JS+canvas画一个圆锥实例代码
2017/12/13 Javascript
详解vue-cli之webpack3构建全面提速优化
2017/12/25 Javascript
tween.js缓动补间动画算法示例
2018/02/13 Javascript
jQuery中元素选择器(element)简单用法示例
2018/05/14 jQuery
vue+axios+element ui 实现全局loading加载示例
2018/09/11 Javascript
[06:13]DOTA2进化论(修改版)
2013/10/08 DOTA
Python实现SSH远程登陆,并执行命令的方法(分享)
2017/05/08 Python
Django实现全文检索的方法(支持中文)
2018/05/14 Python
Python中xml和dict格式转换的示例代码
2019/11/07 Python
Python socket处理client连接过程解析
2020/03/18 Python
Python使用plt.boxplot() 参数绘制箱线图
2020/06/04 Python
Ubuntu 20.04安装Pycharm2020.2及锁定到任务栏的问题(小白级操作)
2020/10/29 Python
Html5 postMessage实现跨域消息传递
2016/03/11 HTML / CSS
amaze ui 的使用详细教程
2020/08/19 HTML / CSS
科沃斯机器人官网商城:Ecovacs
2016/08/29 全球购物
在Java开发中如何选择使用哪种集合类
2016/08/09 面试题
12月小学生校园广播稿
2014/02/04 职场文书
优良学风班总结材料
2014/02/08 职场文书
三分钟演讲稿事例
2014/03/03 职场文书
毕业生学校组织意见
2015/06/04 职场文书
2019年行政人事个人工作总结范本!
2019/07/19 职场文书
创业计划书之都市休闲农庄
2019/12/28 职场文书
分析JVM源码之Thread.interrupt系统级别线程打断
2021/06/29 Java/Android
Golang 字符串的常见操作
2022/04/19 Golang