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 相关文章推荐
一个查看session内容的函数
Oct 09 PHP
如何实现给定日期的若干天以后的日期
Oct 09 PHP
php中定义网站根目录的常用方法
Aug 08 PHP
深入分析php之面向对象
May 15 PHP
关于url地址传参数时字符串有回车造成页面脚本赋值失败的解决方法
Jun 28 PHP
php cli换行示例
Apr 22 PHP
php实现建立多层级目录的方法
Jul 19 PHP
ThinkPHP分页实例
Oct 15 PHP
Zend Framework前端控制器用法示例
Dec 11 PHP
PHP实现的服务器一致性hash分布算法示例
Aug 09 PHP
PHP+Apache实现二级域名之间共享cookie的方法
Jul 24 PHP
PhpStorm连接服务器并实现自动上传功能
Dec 09 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
全国FM电台频率大全 - 5 内蒙古自治区
2020/03/11 无线电
咖啡是不是喝了会上瘾?咖啡是必须品吗!
2021/03/04 新手入门
php cookie的操作实现代码(登录)
2010/12/29 PHP
php dirname(__FILE__) 获取当前文件的绝对路径
2011/06/28 PHP
PHP图像处理类库MagickWand用法实例分析
2015/05/21 PHP
Firefox 无法获取cssRules 的解决办法
2006/10/11 Javascript
Save a File Using a File Save Dialog Box
2007/06/18 Javascript
一个JS的日期格式化算法示例
2013/07/31 Javascript
JS获取select的value和text值的简单实例
2014/02/26 Javascript
Javascript中3种实现继承的方法和代码实例
2014/08/12 Javascript
分享一个自己动手写的jQuery分页插件
2014/08/28 Javascript
浅谈javascript中的instanceof和typeof
2015/02/27 Javascript
jquery实现点击展开列表同时隐藏其他列表
2015/08/10 Javascript
JS排序方法(sort,bubble,select,insert)代码汇总
2016/01/30 Javascript
jQuery使用中可能被XSS攻击的一些危险环节提醒
2016/05/24 Javascript
javascript解决小数的加减乘除精度丢失的方案
2016/05/31 Javascript
js图片切换具体实现代码
2016/10/13 Javascript
JS中使用media实现响应式布局
2017/08/04 Javascript
Vue 实现双向绑定的四种方法
2018/03/16 Javascript
JS扁平化输出数组的2种方法解析
2019/09/17 Javascript
[04:11]DOTA2亚洲邀请赛小组赛第一日 TOP10精彩集锦
2015/01/30 DOTA
[01:33]完美世界DOTA2联赛PWL S3 集锦第二期
2020/12/21 DOTA
python实现的登陆Discuz!论坛通用代码分享
2014/07/11 Python
浅析使用Python操作文件
2017/07/31 Python
python实现简易内存监控
2018/06/21 Python
numpy 对矩阵中Nan的处理:采用平均值的方法
2018/10/30 Python
自学python的建议和周期预算
2019/01/30 Python
Tensorflow 实现释放内存
2020/02/03 Python
django实现模板中的字符串文字和自动转义
2020/03/31 Python
使用python创建生成动态链接库dll的方法
2020/05/09 Python
Python 绘制可视化折线图
2020/07/22 Python
PatPat德国:妈妈的每日优惠
2019/10/02 全球购物
电子专业推荐信范文
2013/11/18 职场文书
质检部经理岗位职责
2014/02/19 职场文书
党员个人总结自评
2015/02/14 职场文书
单身狗福利?Python爬取某婚恋网征婚数据
2021/06/03 Python