Skip to main content

什么是队列

在 Web 应用中,发送邮件、缩放图片、请求外部 API 等操作可能耗时数秒。 若在 HTTP 请求中同步处理,用户必须等到响应返回才能继续。 Laravel 的队列可以让这类耗时操作在后台异步执行。 请求可以立即返回响应,实际处理由 worker 进程另行完成。
队列支持数据库、Redis、Amazon SQS 等多种后端。 开发环境可以使用 sync 驱动,让任务不经队列直接执行。

队列配置

config/queue.php

队列配置集中在 config/queue.php。通过 QUEUE_CONNECTION 环境变量切换驱动。

.env 配置

准备数据库驱动

使用 database 驱动需要保存任务的表。Laravel 11 及以上的新项目默认包含相应迁移,未包含时可运行:

准备 Redis 驱动

使用 redis 驱动请在 config/database.php 配置 Redis 连接,并安装依赖:

SQS Overflow Storage

Amazon SQS 对单条消息大小有限制。若任务负载可能很大,可以将超过部分保存到缓存存储,SQS 中只放引用:
  • 启用 enabled 后,大于 1MB 的负载会被写入指定缓存存储。
  • alwaystrue 时,无论大小都将 SQS 负载写入缓存。
  • delete_after_processing 在任务成功后删除已保存的负载(默认 true)。
  • flush_on_cleartrue 时,在 queue:clear 时会 flush overflow 缓存。为避免影响普通缓存,建议使用独立缓存存储。

创建任务类

make:job 命令

生成 app/Jobs/SendWelcomeEmail.php

任务类结构

实现 ShouldQueue 告诉 Laravel 该任务要走队列异步处理。 Queueable trait 提供了必要的操作方法。
向构造器传入 Eloquent 模型时,Laravel 会只序列化其 ID,运行时再从数据库取回最新数据,让队列负载更轻。

派发任务

dispatch()

在控制器或服务中派发任务:

延迟派发

dispatchAfterResponse()

在 HTTP 响应返回之后立刻执行任务。sync 驱动也可用,适合无需专用 worker 的轻量场景。

指定队列

Queue Routing

在服务提供者 boot() 中通过 Queue::route() 集中配置任务的默认连接 / 队列:
也可以指定接口 / trait / 父类,所有实现或继承者自动应用。多任务批量映射用数组:
Queue Routing 可被任务自身的 onQueue() / onConnection() 覆盖。

队列转发(Queue::forward()

使用 Queue::forward() 可以把任务从一个队列转发到另一个队列或连接。当你想切换队列基础设施而不修改单个任务或调用方代码时非常方便。
要一次转发多个队列,可以传入数组。
如果任务自身显式指定了连接,则任务的指定优先于转发设置。

同步执行(测试 / 开发)

批量派发

使用 Bus::bulk() 一次派发大量独立任务,无需追踪进度或回调:
Bus::bulk() 会按连接与队列名分组批量推送。与 Bus::batch() 不同,没有进度追踪与完成回调,适合大量独立任务的简单批量发送。

任务链

通过任务链可以让多个任务按顺序执行。链中的任务失败时,后续任务不会执行。
还可以添加在整个链完成时或链中任务失败时执行的回调。

任务批处理

使用批处理可以批量派发多个任务并追踪整体进度。首先为 job_batches 表创建迁移。
在任务类中使用 Batchable trait。
通过 Bus::batch() 派发批次,并可注册完成、失败、结束时的回调。
可以使用批次 ID 查看批次的状态。

处理任务

queue:work

启动 worker 处理任务:
指定驱动或队列:
queue:work 是长驻进程。代码变更后请用 queue:restart 重启 worker。生产环境常用 Supervisor 管理。

worker 常用选项

在任务类上写重试配置

使用任务中间件控制执行

任务中间件可将限流、防止重复执行等横切逻辑从 handle() 方法中剥离出来,通过 middleware() 方法以声明式的方式指定。由于不必将逻辑写在任务本体中,多个任务可以更方便地复用同一套控制。
自定义的任务中间件可通过 make:job-middleware Artisan 命令生成。任务中间件同样可用于队列事件监听器Mailable通知

速率限制(RateLimited)

使用 RateLimiter 门面的 for 方法定义限流规则,再将 Illuminate\Queue\Middleware\RateLimited 中间件应用到任务上。
超出限流阈值的任务会根据剩余等待时间自动释放回队列。也可以通过 releaseAfter() 固定重试的间隔秒数,或使用 dontRelease() 直接结束而不再重试。
被释放的任务同样会计入尝试次数(attempts)。请合理配置 #[Tries]retryUntil()
如果使用 Redis,可以选用性能更好的 Illuminate\Queue\Middleware\RateLimitedWithRedis

防止重复执行(WithoutOverlapping)

Illuminate\Queue\Middleware\WithoutOverlapping 可基于任意 key 阻止同一任务被并发执行。对于同一资源一次只希望有一个任务在更新的场景非常有用。
重复的任务会被释放回队列,可用 releaseAfter() 指定重试间隔,用 dontRelease() 让其立即被丢弃。由于底层使用原子锁机制,建议使用 expireAfter() 明确指定锁的有效期,这样即使任务意外失败或超时,锁也不会一直残留。
默认情况下只在同一任务类中防止重复。若希望在不同任务类之间共享锁的 key,可使用 shared() 方法。

抑制连续异常(ThrottlesExceptions)

Illuminate\Queue\Middleware\ThrottlesExceptions 用于与外部 API 等不稳定服务对接的任务:当异常达到指定次数后,会暂停后续执行一段时间。通常与基于时间的重试限制retryUntil())配合使用。
可通过 when() 仅对特定异常进行节流,或用 deleteWhen() 在遇到某类异常时直接删除任务,实现更细粒度的控制。
如果使用 Redis,可用 Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis 更高效地执行节流。

任务释放(Release 中间件)

在特定条件下不执行任务而将其放回队列,可使用 Release 中间件:
Release::unless() 则在条件为 false 时释放。
用闭包表达复杂条件:
Release 也会累加尝试次数。请合理配置 #[Tries]$tries

失败任务处理

准备 failed_jobs 表

任务超过最大重试次数会写入 failed_jobs。若无此表:

失败时的清理

在任务类定义 failed() 方法:

用异常控制不重试

某些异常无需重试。在 bootstrap/app.phpwithExceptions() 中用 dontRetry 指定:
更细粒度可用 dontRetryWhen,回调返回 true 时立即失败:
校验错误或订阅过期等重试也无法成功的异常适合直接失败。

查看失败任务

重试失败任务

删除失败任务

常用驱动

database

无需额外中间件即可上手。任务存 jobs 表,worker 轮询处理。
  • 优点:配置简单,可复用 RDBMS
  • 缺点:数据库压力大,不适合大量任务

redis

生产环境最常用,内存速度快,可支撑大量任务。
  • 优点:速度快、可扩展
  • 缺点:需要 Redis 服务
在生产环境使用 Redis 队列时,可考虑引入 Laravel Horizon,通过仪表盘实时监控队列。

使用 Supervisor 在生产环境运行

在 Linux 上通常使用 Supervisor 保证 queue:work 崩溃后自动重启:
numprocs=2 表示并行启动 2 个 worker。之后:

实战示例:用队列处理邮件发送

1

生成任务类

2

实现任务

3

控制器中派发

4

启动 worker

小结

  • 发送邮件 / SMS
  • 图片、视频的缩放或转码
  • 请求外部 API
  • 生成报表或导出 CSV
  • 发送 Webhook
.env 中设置 QUEUE_CONNECTION=sync,任务将立即执行、不经队列,无需启动 worker 就能调试。
最后修改于 2026年9月15日