调用processorder::dispatch($order)不执行任务,而是由调度总线判断是否实现shouldqueue接口:实现则序列化载荷存入队列后端(如redis或数据库),未实现则同步执行handle();模型仅存类名与主键,执行时重新查询数据库获取最新状态。

任务分发时到底发生了什么
调用 ProcessOrder::dispatch($order) 并不等于执行任务,而是触发 Laravel 的调度总线(Dispatcher)。这个总线会立刻检查任务类是否实现了 ShouldQueue 接口:
- 实现该接口 → 序列化任务载荷(payload),写入配置的队列后端(如 Redis 的
LPUSH queues:default,或数据库jobs表) - 未实现 → 直接同步调用
handle(),完全绕过队列系统
注意:dispatch() 是静态方法,由 Dispatchable trait 提供,它不关心驱动类型,只负责把任务交出去。
为什么模型传进队列后查出来总是“最新状态”
因为 SerializesModels(已整合进 Queueable)不会序列化整个 Eloquent 实例,只存类名和主键 ID。工作进程反序列化时,会重新执行 User::find($id) 查询数据库。
- 这意味着:分发时修改了用户状态,两分钟后任务执行,
$this->user->status是数据库当前值,不是分发那一刻的快照 - 若需固定快照,得手动在构造函数中提取字段值(如
public string $email),而非直接传模型 - 该行为在 Laravel 13 中未改变,只是封装更简洁了
redis 队列 key 冲突导致任务串跑的真实原因
Redis 驱动默认用 queues:default 作为 list 键名。如果两个 Laravel 项目共用同一个 Redis 实例且都用 'queue' => 'default',它们的队列就混在同一个 list 里。
- 解决方式不是改代码,而是改配置:在
config/queue.php的 redis 连接里指定唯一队列名,比如'queue' => 'app1-jobs' - 启动 worker 时也得显式指定:
php artisan queue:work --queue=app1-jobs - 不加
--queue参数时,worker 会 fallback 到配置里的queue值,而不是自动识别任务所属队列
database 驱动下 jobs 表字段的实际作用
jobs 表不是简单存 payload,每个字段都有明确语义:
-
available_at:Unix 时间戳,表示任务最早可被取走执行的时间(用于delay()) -
reserved_at:worker 取出任务时写入的时间戳,非 null 表示该任务已被占用 -
attempts:当前重试次数,失败后自动 +1,超限(retry_after和maxTries控制)则进failed_jobs -
queue字段支持多队列隔离,但必须配合php artisan queue:work --queue=high,default使用,否则只读default
真正容易被忽略的是 reserved_at 和 available_at 的时间精度——它们是整数秒,不是毫秒。如果你依赖 sub-second 延迟,database 驱动不适合。











