生产环境必须用 Redis 驱动:支持原子出队、失败重试与延迟任务;Database 仅适合开发调试,轮询机制易致延迟与连接耗尽;务必配合 Supervisor 守护 worker 进程并正确配置 stopwaitsecs。

队列驱动选 database 还是 redis?
开发阶段用 database 最省事,但上线后必须换 redis 或 supervisor 配合的持久化驱动,否则任务会卡死、重试失效、甚至丢任务。
-
database适合本地调试:任务存进jobs表,php artisan queue:work轮询查表——但轮询间隔默认 3 秒,高并发下延迟明显,且数据库连接容易撑爆 -
redis是生产首选:基于 Pub/Sub 和 List 结构,支持原子出队、失败重试、延迟任务;需装predis/predis或ext-redis扩展,配置里把BROADCAST_DRIVER和QUEUE_CONNECTION都设成redis - 别用
sync模式跑异步逻辑:它只是“假装异步”,实际同步执行,dispatch()一调就阻塞,根本没走队列
dispatch() 后任务没执行?先看这三件事
不是代码写错了,大概率是环境或配置没对齐。
- 检查
.env的QUEUE_CONNECTION是否和你启动的 worker 一致(比如配了redis,却运行php artisan queue:work database) - 确认
php artisan queue:work进程活着:它是一次性命令,退出后任务就停摆;线上必须用supervisor守护,别手动敲命令凑合 - 任务类没实现
ShouldQueue接口?哪怕用了dispatch(),没这个接口 Laravel 就当普通对象处理,不会进队列
延迟任务用 delay() 还是 availableAt()?
二者底层都走 delay 字段,但语义和可维护性差很多。
-
delay(60)表示“60 秒后执行”,直观好懂,适合固定间隔场景(如注册后 1 分钟发欢迎邮件) -
availableAt(now()->addMinutes(5))更灵活,能结合业务时间点计算(比如订单超时未支付,按创建时间 + 30 分钟算),但要注意传入的是DateTimeInterface,别传字符串或时间戳 - 别在
delay()里传负数或超大值:Laravel 不校验,Redis 会静默截断,任务可能永远不触发
任务失败后怎么重试?别只靠 $tries
$tries = 3 只控制重试次数,真正决定“何时重试”和“重试啥”的是 retryUntil() 和 failed() 方法。
-
$tries是硬上限,但每次失败后默认立刻重试,容易打爆下游服务;改用retryAfter(60)设定最小间隔(单位秒) - 想按条件跳过重试?在
failed()里判断错误类型:if ($exception instanceof \GuzzleHttp\Exception\ConnectException) { return; },直接结束,不进failed_jobs表 - 重试日志容易被忽略:Laravel 默认只记一次失败,后续重试失败覆盖前一条;要查完整链路,得自己在
failed()里写日志或发告警
队列不是“扔进去就完事”的黑盒,从驱动选择到失败归因,每层都有隐含约束。最常被跳过的其实是 supervisor 配置里的 stopwaitsecs —— 它决定 worker 关闭前等多久,设太小会导致正在执行的任务被 SIGKILL 强杀,连 failed() 都进不去。










