dispatch()后任务未执行是因为Laravel队列默认不自动运行,需手动启动queue:work进程,且QUEUE_CONNECTION必须设为redis或database而非sync。

dispatch() 调用后任务没执行,不是代码写错了,而是 Laravel 队列默认不自动运行——它只负责把任务塞进 Redis 或数据库,等着你手动拉起消费者进程。
QUEUE_CONNECTION 为什么不能是 sync?
sync 是同步驱动,dispatch() 会立刻在当前请求中执行 handle(),根本不是异步。要真异步,必须改回 database 或 redis:
-
.env中设为QUEUE_CONNECTION=database或QUEUE_CONNECTION=redis - 用
database驱动需先运行php artisan queue:table和php artisan migrate - 用
redis驱动需确认phpredis扩展已启用,且config/database.php中 redis 配置可连通
queue:work 进程为什么一跑就停或不处理新任务?
php artisan queue:work 默认启动后会复用同一个应用实例,长期运行容易内存泄漏、DB 连接超时、配置未刷新。生产环境必须加限制参数:
- 加
--max-jobs=100:每处理 100 个任务后自动重启进程 - 加
--max-time=3600:每运行 1 小时强制重启 - 绝不能裸跑:终端关闭、SSH 断开都会 kill 掉进程,必须用
Supervisor守护
任务里查数据库为什么查不到最新数据?
队列任务序列化时只保存传入的 ID 或简单值,不保存模型实例。你在 dispatch() 时传的是对象,handle() 里拿到的可能是过期状态:
- 错误写法:
$user = $this->user;(假设$this->user是 Eloquent 实例)→ 反序列化失败或数据陈旧 - 正确写法:
$user = User::find($this->userId);,在handle()中实时查库 - 若需关联数据,建议 dispatch 前预取关键字段(如 JSON 字符串),避免 handle 中 N+1 查询
Redis 驱动下任务重复执行怎么防?
Redis 不保证“恰好一次”,网络抖动或 worker 崩溃未及时 ack,会导致任务被重新入队。Laravel 默认重试 3 次,但业务层仍需防御:
- 给任务类加
public $uniqueFor = 3600;,配合uniqueId()方法防止相同参数短时间重复入队 - 关键操作加幂等判断:比如发短信前查
sms_logs表是否已有同order_id记录 - 别依赖
failed()做补偿逻辑——它只在重试耗尽后触发,中间失败不会进这里
最常被忽略的一点:队列任务不在原请求事务内,DB::transaction 不会跨进程生效。如果业务强依赖事务一致性,得在 handle() 里自己控制或改用事件+补偿机制。


















