- Laravel 提供了一个统一的队列 API,支持多种驱动程序(数据库、Redis、SQS 等),并将连接和逻辑队列分离。
- 作业功能允许将繁重的任务移至后台运行,并支持序列化模型、中间件、一次性作业、加密、批处理和字符串。
- 使用 queue:work、Supervisor 和 queue:pause/queue:continue 等命令来管理工作进程,控制重试、超时和优先级。
- 有一个完整的故障、监控和测试生态系统(failed_jobs、queue:retry、queue:monitor、Queue::fake),使生产环境中的队列可靠。

在 Laravel 中,对于任何稍微严肃一点的项目来说,使用队列几乎是必不可少的:发送电子邮件、处理 CSV 文件、生成 PDF、调整图像大小、调用外部 API……如果所有这些操作都在主 HTTP 请求中完成,网站就会变慢,出现 500 错误,用户最终只能绝望地盯着空白屏幕。
好消息是,Laravel 提供了一个强大而灵活的队列基础架构,允许你将繁重的任务委托给后台进程,按优先级管理它们,将它们分组,监控它们,在失败时重试,甚至可以暂时暂停工作进程进行维护。让我们仔细看看(不遗漏任何细节),如何充分利用它,包括如何在生产环境中暂停、恢复和控制其行为。
基本概念:连接、队列和驱动程序
在 Laravel 中,暂停任何操作之前,你需要了解以下两者之间的区别: 队列连接和队列本身。 在 config/queue.php 你有一个数组 connections 在这里定义每个后端: 数据库、Redis、Beanstalkd、SQS、同步、空值、故障转移、RabbitMQ (如果您添加的话),等等。
每个连接可以有多个 逻辑队列 (例如这样的名字) default, emails, high, low, imports该属性 queue 每个连接都指示了如果没有指定其他队列时,作业将进入哪个默认队列。 onQueue()这样你就可以做到以下几点:
- 快速排队 用于电子邮件或短时交易。
- 另一个低优先级项目 适用于处理视频或大型报告等长时间任务。
- 按任务类型划分的队列:
imports,notificaciones,reportes等等。
当你启动 worker 时 php artisan queue:work默认情况下,它监听指定的队列。 默认 在选定的连接上。如果需要设置优先级,可以传递多个以逗号分隔的队列:
php artisan queue:work redis --queue=high,default,low
这样,工作进程会先处理高优先级任务,然后处理默认任务,最后处理低优先级任务,这在系统负载较高时至关重要,因为你不希望一份 30 分钟的报告阻塞高优先级电子邮件。
队列驱动因素和先决条件
Laravel 为多个队列驱动程序提供了统一的 API ,但每个驱动程序都有其特定的功能和依赖项。在讨论暂停工作进程之前,选择合适的驱动程序至关重要。
驱动程序数据库
司机 数据库 它将作业存储在 SQL 表中(通常 jobs它易于设置,非常适合小型项目或开发环境,在这些环境中,您不想使用 Redis 或 SQS。
基本步骤:
- 生成迁移:
php artisan queue:table(或者在新版本中,它以如下形式出现)create_jobs_table默认)。 - 运行迁移:
php artisan migrate. - 配置 QUEUE_CONNECTION=数据库 在
.env并确认config/queue.php该'default' => env('QUEUE_CONNECTION','database').
它既方便又足够强大,但是当队列很多、流量很大时,它可能会成为瓶颈,因为所有数据都通过同一个关系数据库传输。
驱动程序 Redis
Redis驱动程序是生产环境中的标准主力驱动程序。它速度快,支持高级功能(锁定、速率限制、原子锁、Horizon 等),并且可扩展性非常好。
要点:
- 您需要在以下位置配置 Redis 连接:
config/database.php. - 选项
serializerycompression来自 Redis 它们不兼容 使用队列驱动程序。 - 如果你使用 Redis集群队列名称必须包含井号(#),例如:
{default}这样,队列中的所有键都会落入同一个槽位。 - 选择
block_for它允许工作进程在等待任务时阻塞几秒钟,而不是持续轮询: 节省 CPU 并减少对 Redis 服务器的查询。
例如:
'redis' => ,
如果你把 block_for = 0如果希望它能快速响应诸如此类的信号,那么工人将一直处于阻塞状态,直到有工作空缺为止。 SIGTERM 或暂停命令。
其他驱动程序:SQS、Beanstalkd、null、故障转移……
除了数据库和 Redis 之外,Laravel 还支持其他系统:
- 亚马逊SQSAWS 上的托管队列,支持 FIFO邮件分组、去重和高级可见性设置。需要 SDK。
aws/aws-sdk-php. - 豆茎使用该软件包非常快捷方便
pda/pheanstalk. - 同步执行这些任务 inmediatamente 在相同的流程中。在开发或测试中很有用,但在生产环境中,它实际上就相当于完全没有使用队列。
- 空:一旦任务被“调度”,就立即将其丢弃。用于完全禁用队列系统。
- 故障转移这允许您配置多个连接,如果主连接失败,Laravel 会尝试将任务推送到列表中的下一个连接。这非常适合以下环境: 高可用性 这很关键。
在暂停和恢复工作进程的层面上,无论驱动程序是什么,逻辑实际上都是相同的,因为它是由 Artisan 命令和Worker类管理的,而不是由队列后端管理的。
作业创建和模型序列化
Un 工作 在 Laravel 中,它只是一个封装了待执行任务的类,该任务会被放入队列中。它通常使用 Artisan 生成并保存。 app/Jobs.
要创建一个:
php artisan make:job ProcessPodcast
生成的类 器物 ShouldQueue 并且通常使用特征 Dispatchable, InteractsWithQueue, Queueable y SerializesModels关键在于方法。 handle()这就是工人接到工作后要做的事情。
一个特别强大的特性是Eloquent 模型的自动序列化。如果在 Job 构造函数中接受一个模型,Laravel 不会将整个对象添加到队列中,而只会添加其标识符。当任务被处理时,它会从数据库中重建模型,并加载其关联关系。
典型示例(以下使用伪代码,避免逐字重复):
- 建造者收到 播客 $podcast.
- 该特征
SerializesModels它负责保存ID。 - En
handle(AudioProcessor $processor)你负责完成繁重的工作(例如处理音频)。
如果您不希望模型中的关系被序列化(这样有效负载就不会很大),您可以使用 $model->withoutRelations() 或者在 PHP 8 中,该属性 # 在开发商推广的房产上。
处理方法中的依赖注入
另一个优点是您可以 直接将服务注入到处理方法中 (就像控制器一样),因为 Laravel 的 Jobs 服务也使用了服务容器。例如,您可以请求一个 AudioProcessorHTTP 客户端、存储库等,框架会负责解析它们。
如果您需要对所谓的……进行超精细控制 handle你可以用 Container::bindMethod() 在服务提供商中,您可以自己定义注入逻辑,但在大多数情况下,没有必要把事情搞得这么复杂。
作业中间件
就像路由有中间件一样,作业也可以有中间件。 特定中间件 在作业执行前后运行。这大大减少了方法中的重复代码。 handle() 它尤其适用于:
- 限速 使用 Redis。
- 避免 重叠 涉及同一资源的作品。
- 常规例外 适用于不稳定的 API。
- 在特定条件下跳过工作(
Skip中间件)。
中间件通常放置在 app/Jobs/Middleware 并从该方法返回。 middleware() 出自约伯记。概念示例:
public function middleware(): array { return ;
特殊类型的作业:独一无二、加密且锁定。
Laravel 允许您将某些工作标记为 独特的 确保永远不会有两个相同的实例同时排队。这是通过实现接口来实现的。 ShouldBeUnique o ShouldBeUniqueUntilProcessing并支持定义密钥 uniqueId() 以及一段时间 uniqueFor.
这非常适合以下情况:
- 重新计算产品的搜索索引: 每个产品仅限一次 直到完成。
- 与不允许对同一资源同时请求的外部服务进行同步。
Laravel 底层使用了一种 原子锁 缓存(Redis、memcached、数据库等)。您还可以为该锁选择缓存驱动程序。 uniqueVia().
此外,还有接口。 ShouldBeEncrypted,你以此表明完整的工作是 进入队列前请输入您的代码在传递敏感数据时非常有用,因为你不希望数据以明文形式通过 Redis、SQS 或数据库传输。
以不同模式调度、延迟和执行作业
一旦你定义了工作,就可以启动它了。 Job::dispatch() 可以从控制器、事件监听器、Artisan 命令,甚至是闭包中传递参数。 dispatch 他们直接去找约伯的建造者。
有趣的变体:
dispatchIf()ydispatchUnless()将货物整理成一行。delay()因此,该作业必须等到特定日期/时间才能处理。dispatchAfterResponse()oafterResponse()endispatch()闭包会将执行延迟到 HTTP 响应发送完毕之后,但仍然在同一个 PHP 进程内执行。dispatchSync()同步运行作业(不经过队列),但重用同一个类。- 特殊联系,例如
deferredobackground在响应之后执行,可以在同一进程中执行,也可以在新的 PHP 进程中执行。
最后,您可以使用链式方法指定连接和队列。 onConnection() y onQueue()无论是在调度时还是在作业构建器中。
精细控制:重试、退避、超时和错误处理
在 Laravel 中使用队列的一个重要部分是决定重试任务的次数、两次尝试之间的等待时间以及何时将其视为失败。
可供的选择:
--triesenqueue:work设置该工作进程所有任务的最大尝试次数(除非任务本身定义了最大尝试次数)。$triesotries()).$tries或者一种方法tries()在作业类中,用于逐作业控制。retryUntil()意思是:“只要你尝试过,就尽力尝试,直到达到某个时间点,之后就算失败。” 如果它与tries, 命令retryUntil.$maxExceptions当问题是由于反复未处理的异常引起的时,应终止程序。$timeouto--timeout指示作业在工作进程终止子进程之前可以运行多少秒。$failOnTimeout如果超时,则将作业标记为失败。$backoff方法backoff()定义重试之间的等待时间,即使值呈指数级变化。
如果某个任务抛出了一个未捕获的异常,Laravel 将…… 释放回尾部 直到配置的尝试次数用尽为止。您可以通过以下方式手动控制此操作: release($segundos) o fail($exception) 当你想故意将一项任务标记为失败时。
失败的作业、failed_jobs 表和清理
当约伯竭尽全力之后,他就会被认为 失败的工作 Laravel 会将其插入到表中。 failed_jobs (或者如果您配置了 DynamoDB,则为 DynamoDB)。该表存储 UUID、连接、队列、作业负载以及抛出的异常。
有用的命令:
php artisan queue:failed列出失败的案例。php artisan queue:retry <id|all>再试一次。php artisan queue:forget <id>删除一个。php artisan queue:flush全部删除(新版本中可选择按小时删除)。php artisan queue:prune-failed --hours=48自动删除旧记录。
在 Jobs 中,您可以定义一个方法 failed(Throwable $exception) 为了进行额外的清理工作:通知用户、撤销部分操作、写入专用日志等。请注意,这将创建一个新的作业实例来调用。 failed()因此,不要依赖于房产变更 handle().
作业批处理:将作业批量处理并用字符串将它们组合起来
当您需要启动成百上千个相关作业时(例如,分块导入大型 CSV 文件、批量处理文件、清理数据等),建议使用Laravel 的批处理功能。
基本流程:
- 创建表
job_batches同php artisan queue:batches-table && php artisan migrate. - 标记将属于批次的作业,并赋予其特性。
Batchable. - 使用
Bus::batch()发送一组带有回调的作业then,catchyfinally. - 通过以下方式从每个作业访问批处理
$this->batch()并查看是否已取消。 - 可以向正在进行的批次中添加更多作业
$this->batch()->add().
批处理与链式操作完美契合:你可以在批处理中包含链式操作,也可以将批处理作为更大链式操作的元素。当你希望多个链式操作“并行”运行,并在所有链式操作完成后触发特定操作时,这种集成方式尤其有用。
工作执行、优先级和部署
队列的所有奇妙之处都源于你拥有 一名或多名工人 运行命令 queue:work (o queue:listen(尽管后者效率较低)。这些进程是长时间运行的守护进程,它们监听各自的队列。
关键选项 queue:work:
- 连接:
php artisan queue:work redis,queue:work database等等。 - 可乐:
--queue=high,default,low确定优先次序。 - -十一:处理单个作业并退出。
- –max-jobs:处理 X 个作业并终止(以便定期重新启动工作进程并释放内存)。
- –最大时间:运行 N 秒后退出。
- -睡觉:当没有工作机会时等待的秒数。
- ——空时停止:清空队列并干净利落地关闭(非常适合 Docker 环境或一次性作业)。
- -暂停如前所述,每个作业的超时时间(秒)。
- -力即使应用程序处于维护模式,也要处理作业。
由于这些进程需要长时间运行,因此在生产环境中,几乎总是使用类似 Supervisor(在 Linux 系统上)的进程监控工具来启动 X 个工作进程,在进程崩溃时重启它们,并管理日志。一个典型的 Laravel Supervisor 配置块定义了:
- 命令 与
php artisan queue:work ...具体的。 - 进程数 指示要并行启动多少个工作进程。
- 日志路径、用户、自动重启等。
Laravel 中的队列暂停和恢复
接下来我们来谈谈维护中经常很重要的一个问题:如何在不突然停止任务、不留下未完成任务的情况下暂停队列中的工作进程。Laravel 提供了几种机制来实现这一点,有些是较新的,有些是传统的。
暂停:队列:暂停和中断信号
在最新版本中,Laravel 添加了用于暂停和恢复worker 执行的特定命令:
php artisan queue:pause:告诉系统停止接受新任务,但让正在进行的任务完成。php artisan queue:continue:从中断的地方继续处理新任务。
这些命令通过 Laravel 内部使用的缓存机制向工作进程发送信号。因此,即使你的实际缓存队列使用的是其他类型的缓存,正确配置缓存驱动程序(例如 memcached、redis、数据库、文件等)也至关重要。
重要提示:暂停队列或工作进程不会取消或删除已排队的作业;它只是阻止在暂停期间从队列中移除更多作业。
调查中断情况及其对性能的影响
每次 worker 完成任务后,Laravel 都会检查缓存中是否有重启、暂停或继续的信号。这虽然开销很小,但确实存在。如果您非常注重性能,并且确定不需要通过命令暂停或重启 worker,则可以通过调用以下代码禁用此轮询:
Queue::withoutInterruptionPolling();
甚至可以调整静态属性 Illuminate\Queue\Worker 禁用特定触摸(可重启, 可暂停但是,如果你这样做,然后尝试使用 queue:pause o queue:restart工人们 他们不会发现的。这是一个需要仔细考虑的设计决策。
使用 Supervisor 或 Docker 手动暂停
除了 Laravel 自身的命令之外,在生产环境中,进程通常也会以间接的方式“暂停”:
- 停止主管程序 (
supervisorctl stop laravel-worker:*),这会导致工人死亡或停止工作。 - 运用 ——空时停止 在部署过程中,让工作进程完成队列中的任务,然后关闭进程。
- 在 Docker 中,让容器处理
--stop-when-empty然后它就关机了。
与 queue:pause 完全停止进程后,作业将停止处理,直到工作进程重新启动。 内部暂停 你可以将进程保存在内存中,等待命令执行。 continue这对于快速回滚部署或短期维护非常有用。
数据库事务和提交后事件
一个典型的错误:在数据库事务中调度任务。如果不小心,工作进程可能会在提交操作完成之前执行任务,导致你期望的模型或数据尚未存在于数据库中。
为了避免这种尴尬局面,你有两种选择:
- 激活
'after_commit' => true在队列连接配置中,任何在事务中分派的作业都会在内部被延迟,直到得到确认。 - 使用按作业方法
afterCommit()obeforeCommit()调度时,强制仅在特定作业中执行此行为。
如果事务被撤销,则事务期间分派的作业将被丢弃,而不会进入队列,这正是为了保持一致性通常所希望的。
监控、队列清理和测试
一旦队列真正开始运行,仅仅发布作业然后置之不理是不够的;你应该监控和测试一切的运行情况。
实用工具和命令:
queue:clear清空特定队列(SQS、Redis、数据库)。queue:monitor监控特定队列中的作业数量并触发事件QueueBusy如果它们超过阈值。- 招聘活动 如
Queue::before,Queue::after,Queue::loopingyQueue::failing挂钩自定义逻辑(日志、指标、回滚等)。 - 测试中的假货 运用
Queue::fake()yBus::fake()验证作业、字符串或批处理是否已分派,而无需实际执行它们。
在测试中,您可以声明“此作业已排队 X 次”、“包含这些作业的链已按此顺序调度”或“没有特定作业排队”,这为复杂的流程提供了很大的安全性。
整个生态系统——包括 Redis 或 SQS 等驱动程序、精心设计的作业、用于微调的中间件、批处理、链式调用、重试配置、Supervisor 管理的 worker 以及诸如此类的命令—— queue:pause y queue:continue— Laravel 让您能够精确到毫米地掌控后台处理,从典型的电子邮件发送到具有故障转移的分布式队列架构,确保您的应用程序在 HTTP 层继续快速响应,同时在底层执行繁重的工作,而不会阻塞用户。