1、为什么使用队列?
异步、重试
2、什么情况使用队列?
(1)、耗时比较久的,比如上传一个文件后进行一些格式的转化等。
(2)、需要保证送达率的,比如发送短信,因为要调用别人的 api,总会有几率失败,那么为了保证送达,重试就必不可少了。
使用队列的时候一定要想明白一个问题,这个任务到底是不是可以异步,如果因为异步会导致问题,那么就要放弃使用队列。
3、什么时候使用 queue:listen 什么时候使用 queue:work?
Laravel 5.3 的文档已经不写 queue:listen 这个指令怎么用了,所以你可以看出来可能官方已经不怎么建议使用 queue:listen 了,但是在本地调试的时候要使用 queue:listen,因为 queue:work 在启动后,代码修改,queue:work 不会再 Load 上下文,但是 queue:listen 仍然会重新 Load 新代码。
其余情况全部使用 queue:work 吧,因为效率更高。
4、命令讲解
php artisan queue:work --daemon --quiet --queue=default --delay=3 --sleep=3 --tries=3
--daemon
总体来说,在 supervisor 中一般要加这个 option,可以节省 CPU 使用。
--quiet
不输出任何内容
--delay=3
一个任务失败后,延迟多长时间后再重试,单位是秒。这个值的设定我个人建议不要太短,因为一个任务失败(比如网络原因),重试时间太短可能会出现连续失败的情况。
--sleep=3
去 Redis 中拿任务的时候,发现没有任务,休息多长时间,单位是秒。这个值的设定要看你的任务是否紧急,如果是那种非常紧急的任务,不能等待太长时间。
--tries=3
定义失败任务最多重试次数。这个值的设定根据任务的重要程度来确定,一般 3 次比较适合。
5、延迟分发
如果你想延迟你的队列任务的执行,你可以在分发任务的时候使用 delay 方法。例如,让我们详细说明一个十分钟之后才会执行的任务:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use \App\Jobs\GoodsInfoToLog;
use phpDocumentor\Reflection\Types\Array_;
class JobProcessController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
$chineseName = array('枯', '藤', '老', '树', '昏', '鸦', '小', '桥', '流', '水', '人', '家', '古', '道', '西', '风', '瘦', '马');
$name = $chineseName[rand(0, 17)] . $chineseName[rand(0, 17)] . $chineseName[rand(0, 17)];
$data = array(
'price' => rand(1, 1000),
'title' => $name,
);
GoodsInfoToLog::dispatch($data)->delay(now()->addSeconds(10));
GoodsInfoToLog::dispatch($data)->delay(now()->addMinutes(1));
}
}
6、分发任务到指定队列
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use \App\Jobs\GoodsInfoToLog;
use phpDocumentor\Reflection\Types\Array_;
class JobProcessController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
$chineseName = array('枯', '藤', '老', '树', '昏', '鸦', '小', '桥', '流', '水', '人', '家', '古', '道', '西', '风', '瘦', '马');
$name = $chineseName[rand(0, 17)] . $chineseName[rand(0, 17)] . $chineseName[rand(0, 17)];
$data = array(
'price' => rand(1, 1000),
'title' => $name,
);
GoodsInfoToLog::dispatch($data)->delay(now()->addSeconds(50)); //延时10秒
GoodsInfoToLog::dispatch($data)->delay(now()->addMinutes(2)); //延时1分钟
GoodsInfoToLog::dispatch($data)->onQueue('test')->delay(now()->addSeconds(50)); //延时10秒
GoodsInfoToLog::dispatch($data)->onQueue('test')->delay(now()->addMinutes(2)); //延时1分钟
}
}
普通推送进去只是一个普通的list数据结构类型,如果是延时或者失败队列是一个zset有序集合
7、最大尝试次数
在一个任务重指定最大尝试次数可以通过 Artisan 命令的 --tries 选项 指定:
php artisan queue:work --tries=3
你可能想通过任务类自身对最大任务尝试次数进行一个更颗粒化的处理。如果最大尝试次数是在任务类中定义的,它将优先于命令行中的值提供:
/**
* 任务可以尝试的最大次数。
*
* @var int
*/
public $tries = 5;
8、基于时间尝试
作为另外一个选择来定义任务在失败前会尝试多少次,你可以定义一个任务超时时间。这样的话,在给定的时间范围内,任务可以无限次尝试。要定义一个任务的超时时间,在你的任务类中新增一个 retryUntil 方法:
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use function PHPSTORM_META\elementType;
class GoodsInfoToLog implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $goods;
/**
* 任务可以尝试的最大次数。
*
* @var int
*/
public $tries = 100;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($goods)
{
$this->goods = $goods;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$goods = $this->goods;
$goods['num'] = $this->attempts();
Log::channel('myapplog')->info($goods);
if ($this->attempts() < 26) {
DB::table('test')->insert(array('title' => $goods['title'], 'price' => $goods['price'], 'create_at' => date("Y-m-d H:i:s"), 'push_at' => date("Y-m-d H:i:s")));
} else {
DB::table('queue_test')->insert(array('title' => $goods['title'], 'price' => $goods['price'], 'create_at' => date("Y-m-d H:i:s"), 'push_at' => date("Y-m-d H:i:s")));
}
}
/**
* 定义任务超时时间
*
* @return \DateTime
*/
public function retryUntil()
{
return now()->addSeconds(30);
}
// /**
// * 任务失败的处理过程
// *
// * @param Exception $exception
// * @return void
// */
// public function failed(Exception $exception)
// {
// // 给用户发送任务失败的通知,等等……
// }
}
在这里轮询限定是100次,但是执行重试30s超时就算是失败了
9、任务过期
在你的 config/queue.php 配置文件中,每个队列连接都定义了一个 retry_after 选项。这个选项指定了队列连接在重试一个任务前应该等它执行多久。例如,如果 retry_after 的值设置为 90 ,那么任务在执行了 90 秒后将会被放回队列而不是删除它。一般情况下,你应该将 retry_after 的值设置为你认为你的任务可能会执行需要最长时间的值。
10、任务超时
queue:work Artisan 命令包含一个 --timeout 选项。 --timeout 选项指定了 Laravel 的队列主进程在中止一个执行任务的子进程之前需要等到多久。有时一个子进程可能会因为各种原因「冻结」,比如一个外部的 HTTP 请求失去响应。 --timeout 选项会移除那些超过指定时间被冻结的进程。
php artisan queue:work --timeout=60
<?php
namespace App\Jobs;
class ProcessPodcast implements ShouldQueue
{
/**
* 任务可以执行的最大秒数 (超时时间)。
*
* @var int
*/
public $timeout = 120;
}
retry_after 配置项和 --timeout 命令行配置并不同,但将它们同时使用可以确保任务不会丢失并且任务只会成功执行一次。
注意:--timeout 的值应该比你在 retry_after 中配置的值至少短几秒。这会确保处理器永远会在一个任务被重试之前中止。如果你的 --timeout 值比 retry_after 的值长的话,你的任务可能会被执行两次。
11、处理失败的任务
Laravel 包含了一种方便的方法来指定任务应该尝试的最大次数。如果一个任务已经到达了最大尝试次数,它就会被插入到 failed_jobs 数据库表中。
可以直接在任务类中定义 failed 方法,允许你在任务失败时执行针对于该任务的清理工作。 这是向用户发送警报或恢复任务执行的任何操作的绝佳位置。导致任务失败的 Exception 将被传递给 failed 方法:
<?php
namespace App\Jobs;
use Exception;
use App\Podcast;
use App\AudioProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class ProcessPodcast implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
protected $podcast;
/**
* 创建任务实例
*
* @param Podcast $podcast
* @return void
*/
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast;
}
/**
* 执行任务
*
* @param AudioProcessor $processor
* @return void
*/
public function handle(AudioProcessor $processor)
{
// 上传播客……
}
/**
* 任务失败的处理过程
*
* @param Exception $exception
* @return void
*/
public function failed(Exception $exception)
{
// 给用户发送任务失败的通知,等等……
}
}
重试失败的任务,要想查看所有被放入 failed_jobs 数据表中的任务,你可以使用 Artisan 命令 queue:failed :
php artisan queue:failed