什么是队列
在 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 的负载会被写入指定缓存存储。 always为true时,无论大小都将 SQS 负载写入缓存。delete_after_processing在任务成功后删除已保存的负载(默认true)。flush_on_clear为true时,在queue:clear时会 flush overflow 缓存。为避免影响普通缓存,建议使用独立缓存存储。
创建任务类
make:job 命令
app/Jobs/SendWelcomeEmail.php。
任务类结构
ShouldQueue 告诉 Laravel 该任务要走队列异步处理。
Queueable trait 提供了必要的操作方法。
派发任务
dispatch()
在控制器或服务中派发任务:延迟派发
dispatchAfterResponse()
在 HTTP 响应返回之后立刻执行任务。sync 驱动也可用,适合无需专用 worker 的轻量场景。
指定队列
Queue Routing
在服务提供者boot() 中通过 Queue::route() 集中配置任务的默认连接 / 队列:
Queue Routing 可被任务自身的
onQueue() / onConnection() 覆盖。队列转发(Queue::forward())
使用 Queue::forward() 可以把任务从一个队列转发到另一个队列或连接。当你想切换队列基础设施而不修改单个任务或调用方代码时非常方便。
如果任务自身显式指定了连接,则任务的指定优先于转发设置。
同步执行(测试 / 开发)
批量派发
使用Bus::bulk() 一次派发大量独立任务,无需追踪进度或回调:
Bus::bulk() 会按连接与队列名分组批量推送。与 Bus::batch() 不同,没有进度追踪与完成回调,适合大量独立任务的简单批量发送。任务链
通过任务链可以让多个任务按顺序执行。链中的任务失败时,后续任务不会执行。任务批处理
使用批处理可以批量派发多个任务并追踪整体进度。首先为job_batches 表创建迁移。
Batchable trait。
Bus::batch() 派发批次,并可注册完成、失败、结束时的回调。
处理任务
queue:work
启动 worker 处理任务:worker 常用选项
在任务类上写重试配置
使用任务中间件控制执行
任务中间件可将限流、防止重复执行等横切逻辑从handle() 方法中剥离出来,通过 middleware() 方法以声明式的方式指定。由于不必将逻辑写在任务本体中,多个任务可以更方便地复用同一套控制。
速率限制(RateLimited)
使用RateLimiter 门面的 for 方法定义限流规则,再将 Illuminate\Queue\Middleware\RateLimited 中间件应用到任务上。
releaseAfter() 固定重试的间隔秒数,或使用 dontRelease() 直接结束而不再重试。
Illuminate\Queue\Middleware\RateLimitedWithRedis。
防止重复执行(WithoutOverlapping)
Illuminate\Queue\Middleware\WithoutOverlapping 可基于任意 key 阻止同一任务被并发执行。对于同一资源一次只希望有一个任务在更新的场景非常有用。
releaseAfter() 指定重试间隔,用 dontRelease() 让其立即被丢弃。由于底层使用原子锁机制,建议使用 expireAfter() 明确指定锁的有效期,这样即使任务意外失败或超时,锁也不会一直残留。
默认情况下只在同一任务类中防止重复。若希望在不同任务类之间共享锁的 key,可使用
shared() 方法。抑制连续异常(ThrottlesExceptions)
Illuminate\Queue\Middleware\ThrottlesExceptions 用于与外部 API 等不稳定服务对接的任务:当异常达到指定次数后,会暂停后续执行一段时间。通常与基于时间的重试限制(retryUntil())配合使用。
when() 仅对特定异常进行节流,或用 deleteWhen() 在遇到某类异常时直接删除任务,实现更细粒度的控制。
Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis 更高效地执行节流。
任务释放(Release 中间件)
在特定条件下不执行任务而将其放回队列,可使用Release 中间件:
Release::unless() 则在条件为 false 时释放。
失败任务处理
准备 failed_jobs 表
任务超过最大重试次数会写入failed_jobs。若无此表:
失败时的清理
在任务类定义failed() 方法:
用异常控制不重试
某些异常无需重试。在bootstrap/app.php 的 withExceptions() 中用 dontRetry 指定:
dontRetryWhen,回调返回 true 时立即失败:
查看失败任务
重试失败任务
删除失败任务
常用驱动
database
无需额外中间件即可上手。任务存jobs 表,worker 轮询处理。
- 优点:配置简单,可复用 RDBMS
- 缺点:数据库压力大,不适合大量任务
redis
生产环境最常用,内存速度快,可支撑大量任务。- 优点:速度快、可扩展
- 缺点:需要 Redis 服务
使用 Supervisor 在生产环境运行
在 Linux 上通常使用 Supervisor 保证queue:work 崩溃后自动重启:
numprocs=2 表示并行启动 2 个 worker。之后:
实战示例:用队列处理邮件发送
1
生成任务类
2
实现任务
3
控制器中派发
4
启动 worker
小结
何时应使用队列
何时应使用队列
- 发送邮件 / SMS
- 图片、视频的缩放或转码
- 请求外部 API
- 生成报表或导出 CSV
- 发送 Webhook
开发小技巧
开发小技巧
在
.env 中设置 QUEUE_CONNECTION=sync,任务将立即执行、不经队列,无需启动 worker 就能调试。常用命令
常用命令