注意:这个特性要求你的应用可以使用 Redis 服务器.
如果你的应用使用了 Redis,你可以通过时间或并发限制你的队列任务。当你的队列任务通过同样有速率限制的 API 使用时,这个特性将很有帮助。
例如,使用 throttle
方法,你可以限制一个给定类型的任务每 60 秒只执行 10 次。如果没有获得锁,一般情况下你应该将任务放回队列以使其可以被稍后重试。
Redis::throttle('key')->allow(10)->every(60)->then
(function () {
}, function () {
return $this->release(10);
});
Tip:在上述的例子里,key
可以是任何你想要限制频率的任务类型的唯一识别字符串。例如,使用构件基于任务类名的 key,或它操作的 Eloquent 模型的 ID。
注意:将受限制的作业释放回队列,仍然会增加工作的总数 attempts
。
或者,你可以指定一个任务可以同时执行的最大数量。在如下情况时这会很有用处:当一个队列中的任务正在修改资源时,一次只能被一个任务修改。例如,使用 funnel
方法,你可以限制一个给定类型的任务一次只能执行一个处理器:
Redis::funnel('key')->limit(1)->then(function () {
}, function () {
return $this->release(10);
});
Tip:当使用频率限制时,任务执行成功的尝试的次数可能会难以确定。所以,将频率限制与 时间限制 组合是很有作用的。
如果在任务执行的时候出现异常,任务会被自动释放到队列中以再次尝试。任务将会一直被释放直到达到应用允许的最大重试次数。最大重试的数值由 queue:work
Artisan 命令的 --tries
选项定义,或者在任务类中定义。更多执行队列处理器的信息可以 在以下找到 。
你也可以直接调用闭包,而不是将任务类调度到队列中。这对于需要执行的快速、简单的任务非常有用:
$podcast = App\Podcast::find(1);
dispatch(function () use ($podcast) {
$podcast->publish();
});
将闭包分派给队列时,闭包的代码内容将以加密方式签名,因此无法在传输过程中对其进行修改。
Laravel 包含了一个队列处理器以将推送到队列中的任务执行。你可以使用 queue:work
Artisan 命令运行处理器。 注意一旦 queue:work
命令开始执行,它会一直运行直到它被手动停止或终端被关闭。
php artisan queue:work
Tip:要使 queue:work
进程一直在后台运行,你应该使用进程管理器比如 Supervisor 来确保队列处理器不会停止运行
记住,队列处理器是一个常驻的进程并且在内存中保存着已经启动的应用状态。因此,它们并不会在启动后注意到你代码的更改。所以,在你的重新部署过程中,请记得 重启你的队列处理器.
你也可以具体说明队列处理器应该使用哪个队列连接。 传递给 work
的连接名应该与你的 config/queue.php
配置文件中定义的连接之一相符。
php artisan queue:work redis
你甚至可以自定义你的队列处理器使其只执行连接中指定的队列。例如,如果你的所有邮件都由 redis
连接的 emails
队列处理,你可以使用如下的命令启动一个仅执行此队列的处理器:
php artisan queue:work redis --queue=emails
--once
选项用于使队列处理器只处理队列中的单一任务。
php artisan queue:work --once
--stop-when-empty
选项可用于处理队列处理器处理所有作业然后优雅地退出。如果您希望在队列为空后关闭容器,则在 Docker 容器中运行 Laravel 队列时,此选项很有用:
php artisan queue:work --stop-when-empty
后台驻留的队列处理器不会在执行完每个任务后「重启」框架。因此,你应该在每个任务完成后释放任何占用过大的资源。例如,如果你正在用 GD 库执行图像处理,你应该在完成后使用 imagedestroy
释放内存。
有时你可能想确定队列执行的优先顺序。例如在 config/queue.php
中你可以将 redis
连接的 queue
队列的优先级从 default
设置为 low
。然而, 偶尔你也想像如下方式将一个任务推送到 high
队列:
dispatch((new Job)->onQueue('high'));
要运行一个处理器来确认 low
队列中的任务在全部的 high
队列任务完成后才继续执行,你可以传递一个逗号分隔的队列名列表作为 work
命令的参数。
php artisan queue:work --queue=high,low
因为队列处理器是常驻进程,他们在重启前不会应用你代码的更改。因此,部署使用队列处理器的应用最简单的方法是在部署进程中重启队列处理器。你可以平滑地重启所有队列处理器通过使用 queue:restart
方法:
php artisan queue:restart
这个命令将会引导所有的队列处理器在完成当前任务后平滑「中止」,这样不会有丢失的任务。由于在执行 queue:restart
后队列处理器将会中止,所以你应该运行一个进程管理器例如 Supervisor 来自动重启队列处理器。
Tip:队列使用 缓存 存储重启信号,所以你应该确定在使用这个功能之前配置好缓存驱动。
在你的 config/queue.php
配置文件中,每个队列连接都定义了一个 retry_after
选项。这个选项指定了队列连接在重试一个任务前应该等它执行多久。例如,如果 retry_after
的值设置为 90
,那么任务在执行了 90 秒后将会被放回队列而不是删除它。一般情况下,你应该将 retry_after
的值设置为你认为你的任务可能会执行需要最长时间的值。
注意:只有在 Amazon SQS 中不存在 retry_after
这个值。 SQS将会以 AWS 控制台配置的 默认可见超时值 作为重试任务的依据。
queue:work
Artisan 命令包含一个 --timeout
选项。 --timeout
选项指定了 Laravel 的队列主进程在中止一个执行任务的子进程之前需要等到多久。有时一个子进程可能会因为各种原因「冻结」,比如一个外部的 HTTP 请求失去响应。 --timeout
选项会移除那些超过指定时间被冻结的进程。
php artisan queue:work --timeout=60
retry_after
配置项和 --timeout
命令行配置并不同,但将它们同时使用可以确保任务不会丢失并且任务只会成功执行一次。
注意:--timeout
的值应该比你在 retry_after
中配置的值至少短几秒。这会确保处理器永远会在一个任务被重试之前中止。如果你的 --timeout
值比 retry_after
的值长的话,你的任务可能会被执行两次。
当任务在队列中可用时,处理器将会一直无间隔地处理任务。 然而, sleep
选项定义了如果没有新任务的时候处理器将会「睡眠」多长时间。在处理器睡眠时,它不会处理任何新任务 —— 任务将会在队列处理器再次启动后执行。
php artisan queue:work --sleep=3
Supervisor 是 Linux 操作系统下中的一个进程监控器,它可以在queue:work
挂掉时自动重启之。在 Ubuntu 上安装 Supervisor,你可以使用如下命令:
sudo apt-get install supervisor
{小提醒}如果觉得配置 Supervisor 难于登天,可以考虑使用Laravel Forge,它将自动为你的 Laravel 项目安装和配置Supervisor。
Supervisor 的配置文件通常位于 /etc/supervisor/conf.d
目录下。在该目录中,你可以创建任意数量的配置文件,用来控制 supervisor 将如何监控你的进程。例如,创建一个laravel-worker.conf
文件使之启动和监控一个 queue:work
进程:
[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
numprocs=8
redirect_stderr=true
stdout_logfile=/home/forge/app.com/worker.log
在这个例子中,numprocs
指令将指定 Supervisor 运行 8 个 queue:work
进程并对其进行监控,如果它们挂掉就自动重启它们。你应该更改command
选项中的queue:work sqs
部分以表示你所需的队列连接。
配置文件创建完毕后,你就可以使用如下命令更新 Supervisor 配置并启动进程了:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-worker:*
获取关于 Supervisor 的更多信息,可以查阅Supervisor 文档.
有时你的队列化任务会执行失败。放平心态,好事多磨。 Laravel 包含了一种方便的方法来指定任务应该尝试的最大次数。如果一个任务已经到达了最大尝试次数,它就会被插入到failed_jobs
数据库表中。要创建 failed_jobs
数据库迁移表,你可以使用 queue:failed-table
命令:
php artisan queue:failed-table
php artisan migrate
然后,当你运行 queue worker,你应该使用queue:work
命令中的--tries
开关指定应尝试运行任务的最大次数。 如果你没有为 --tries
选项指定一个值,那么任务将只被尝试一次::
php artisan queue:work redis --tries=3
你可以直接在任务类中定义 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)
如果你想在任务失败时注册一个可调用的事件,你可以使用Queue::failing
方法。该事件是通过 email 或 Slack 通知你团队的绝佳时机。例如,我们可以在 Laravel 中的 AppServiceProvider
中附加一个回调事件:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Queue;
use Illuminate\Queue\Events\JobFailed;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
* 启动任意服务。
* @return void
public function boot()
Queue::failing(function (JobFailed $event) {
});
* 注册服务提供者。
* @return void
public function register()
要想查看所有被放入 failed_jobs
数据表中的任务,你可以使用 Artisan 命令 queue:failed
:
php artisan queue:failed
queue:failed
命令会列出任务 ID ,队列,以及失败的时间。任务 ID 可能会被用于重试失败的任务。例如,要重试一个任务 ID 为 5
的任务,使用如下命令:
php artisan queue:retry 5
要重试所有失败的任务,执行 queue:retry
命令,将 all
作为 ID 传入:
php artisan queue:retry all
如果你想删除一个失败的任务,使用 queue:forget
命令:
php artisan queue:forget 5
要清空所有失败的任务,使用 queue:flush
命令:
php artisan queue:flush
在向任务中注入 Eloquent 模型时,模型被放入队列前将被自动序列化并在执行任务时还原。但是,如果在任务等待执行时删除了模型,任务可能会失败并抛出 ModelNotFoundException
。
为了方便,你可以选择设置任务的 deleteWhenMissingModels
属性为 true
来自动地删除缺失模型的任务。
* 如果模型缺失即删除任务。
* @var bool
public $deleteWhenMissingModels = true;
通过在 Queue
facade中使用 before
和 after
方法,你可以指定一个队列任务被执行前后的回调。这些回调是添加额外的日志或增加统计的绝好时机。通常,你应该在 服务提供者中调用这些方法。例如,我们可以使用 Laravel 的 AppServiceProvider
:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\ServiceProvider;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Events\JobProcessing;
class AppServiceProvider extends ServiceProvider
* 引导启动任意应用服务。
* @return void
public function boot()
Queue::before(function (JobProcessing $event) {
});
Queue::after(function (JobProcessed $event) {
});
* 注册服务提供者。
* @return void
public function register()
在 Queue
facade 使用 looping
方法可以在处理器尝试获取任务之前执行回调。例如,你也许想用一个闭包来回滚之前失败的任务尚未关闭的事务:
Queue::looping(function () {
while (DB::transactionLevel() > 0) {
DB::rollBack();
});
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。