队列驱动
# 同步队列
sync
# 数据库
database
# 第三方队列服务
beanstalkd
sqs
redis
# 不使用队列
null
配置
# 任务过期 任务在执行了 90 秒后将会被放回队列而不是删除它,设置为你认为你的任务可能会执行需要最长时间
retry_after=90
生成任务表
# 队列表
php artisan queue:table
# 生成队列失败表
php artisan queue:failed-table
# 执行数据迁移
php artisan migrate
创建任务类
php artisan make:job ProcessPodcast
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
//应该处理任务的队列连接.
public $connection = 'sqs';
//任务可以执行的最大秒数 (超时时间)。
public $timeout = 120;
//任务可以尝试的最大次数。
public $tries = 5;
//基于时间的尝试 在给定的时间范围内,任务可以无限次尝试
public function retryUntil()
{
return now()->addSeconds(5);
}
//如果模型缺失即删除任务。
public $deleteWhenMissingModels = true;
protected $podcast;
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast;
}
// 执行任务
public function handle(AudioProcessor $processor)
{
// 处理上传播客...
}
//任务失败的处理过程
public function failed(Exception $exception)
{
// 给用户发送任务失败的通知,等等……
}
}
频率限制
# 使用 throttle 方法,你可以限制一个给定类型的任务每 60 秒只执行 10 次
Redis::throttle('key')->allow(10)->every(60)->then(function () {
// 任务逻辑...
}, function () {
// 无法获得锁...
return $this->release(10);
});
# 可以限制一个给定类型的任务一次只能执行一个处理器
Redis::funnel('key')->limit(1)->then(function () {
// 任务逻辑...
}, function () {
// 无法获得锁...
return $this->release(10);
});
分发任务
$job=new \App\Jobs\ProcessPodcast(\Illuminate\Support\Facades\Auth::user());
dispatch($job);
\App\Jobs\ProcessPodcast::dispatch(['name' =>'yanjiaqing']);
# 延迟10分钟分发
ProcessPodcast::dispatch($podcast)->delay(now()->addMinutes(10));
# 同步调度 队列任务将不会排队,并立即在当前进程中运行
ProcessPodcast::dispatchNow($podcast);
# 分发任务到指定连接
ProcessPodcast::dispatch($podcast)->onConnection('sqs');
# 分发任务到指定队列
ProcessPodcast::dispatch($podcast)->onQueue('processing');
# 排队闭包
$podcast = App\Podcast::find(1);
dispatch(function () use ($podcast) {
$podcast->publish();
});
# 队列优先级
dispatch((new Job)->onQueue('high'));
链连接和队列
ProcessPodcast::withChain([
new OptimizePodcast,
new ReleasePodcast
])->dispatch()->allOnConnection('redis')->allOnQueue('podcasts');
任务执行前后的处理
# App/Providers/AppServiceProvider
public function boot()
{
Queue::before( function (JobProcessing $event) {
$event->connectionName
$event->job
$event->job->payload()
Log::info("处理任务前");
});
Queue::after( function (JobProcessed $event) {
Log::info("处理任务后");
});
# 任务失败事件
Queue::failing(function (JobFailed $event) {
// $event->connectionName
// $event->job
// $event->exception
});
}
运行队列处理器
php artisan queue:work redis --queue=emails
# 指定链接
redis
# 指定队列
emails
# 只处理队列中的单一任务。
--once
# 处理所有队列的任务然后退出 docker容器好用
--stop-when-empty
# 要运行一个处理器来确认 low 队列中的任务在全部的 high 队列任务完成后才继续执行
--queue=high,low
# 处理器超时 短于retry_after 配置项
--timeout=60
# 队列进程睡眠时间
--sleep=3
# 最大尝试次数
--tries=3
# 以守护进程模式运行工作程序(不推荐)
--daemon
# 延迟失败作业的时间量
--delay=0
# 强制工人在维护模式下运行
--force
# 内存限制(以兆字节为单位)
--memory=128
重试失败的任务
# 查看所有被放入 failed_jobs 数据表中的任务
php artisan queue:failed
# 要重试一个任务 ID 为 5 的任
php artisan queue:retry 5
# 要重试所有失败的任务,执行 queue:retry 命令
php artisan queue:retry all
# 如果你想删除一个失败的任务
php artisan queue:forget 5
# 要清空所有失败的任务
php artisan queue:flush
队列处理器 & 部署
php artisan queue:restart
三种消费队列方式
1 queue:work - 这是一个新的后台进程(不再需要 daemon 标记), 这种方式运行,框架只会启动一次,并保持循环去消费队列,除非出现异常否则该进程将无限时间运行下去。这种方式消耗的 cpu 和 内存 都比 queue:listen 要少,因为在整个生命周期中框架一直是在保持运行状态。同时,使用该方法时如果更新了代码,记得使用 queue:restart 来重启。
2 queue:work --once - 该方法会启动框架,运行 job,然后销毁掉。在开发和测试代码的时候使用比较合适,因为每次都会加载一遍代码嘛。
3 queue:listen - 这种方式运行,框架每次都会启动,运行job,然后关闭,然后再次启动框架,运行job,然后关闭,这样一直循环(每次运行完一次都会完全释放掉运行时的内存和进程)。所以这种方式你不用担心代码的热更新,不用去重启 queue,随之而来的另外一个好处是不用去担心 queue:work 带来的内存泄漏。
注意 从 5.3 版本开始 --daemon 这个参数已经不再起作用了
Supervisor 安装
# ubuntu
sudo apt-get install supervisor
配置 Supervisor
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /home/forge/app.com/artisan queue:work sqs --sleep=3 --tries=3
autostart=true
autorestart=true
user=forge
# 运行 8 个 queue:work 进程并对其进行监控
numprocs=8
redirect_stderr=true
stdout_logfile=/home/forge/app.com/worker.log
在 Queue facade 使用 looping 方法可以在处理器尝试获取任务之前执行回调
Queue::looping(function () {
while (DB::transactionLevel() > 0) {
DB::rollBack();
}
});