队列需任务类、分发动作、消费者进程三者协同;laravel中需手动实现shouldqueue接口,避免传不可序列化对象,配置queue_connection非sync,用queue:work配合supervisor运行,失败任务需建failed_jobs表并配置重试策略。

队列不是“配好就能自动跑”的东西,它由三块拼图组成:任务类、分发动作、消费者进程;缺任何一块,dispatch() 都只会静默失败或退化成同步执行。
怎么生成一个可入队的任务类
用 php artisan make:job 创建基础结构,但别以为生成完就万事大吉:
- Laravel 9+ 默认不自动加
implements ShouldQueue,必须手动补上,否则dispatch()会直接同步调用handle() - 构造函数里别传
Request、UploadedFile或 DB 连接实例——这些对象无法被序列化,入队时会抛出Serialization of 'Closure' is not allowed类错误 - 如果需要延迟执行,不要在构造函数里算时间,而是在分发时用
delay():SendWelcomeEmail::dispatch($user)->delay(now()->addMinutes(5))
为什么 dispatch() 没反应?检查这三处配置
常见现象是调了 dispatch() 却没进 Redis 或 jobs 表,页面也无报错——大概率卡在这几个地方:
-
.env中QUEUE_CONNECTION必须设为redis、database等非sync值;sync是默认值,仅用于本地调试,它根本不会走队列系统 - 数据库驱动下,确认已运行
php artisan queue:table和php artisan migrate,否则jobs表不存在,写入直接静默丢弃 - Redis 驱动下,检查
config/database.php中redis.default是否可连,且config/queue.php里redis.connection值是否与之匹配(默认是default)
怎么让队列真正跑起来
php artisan queue:work 不是“一键启动”命令,它本身是个常驻进程,需配合守护机制才适合生产环境:
- 本地测试可直接运行
php artisan queue:work --verbose查看实时日志;加--once可只处理一个任务后退出,方便调试 - 生产环境必须用 Supervisor 管理进程,否则终端关闭或 SSH 断开后,
queue:work就停了;Supervisor 配置中关键项包括autostart=true、autorestart=true、numprocs=3(并发 worker 数) - 若要用优先级(比如支付任务 > 日志记录),分发时指定队列名:
dispatch(new PaymentJob())->onQueue('payment'),再用php artisan queue:work --queue=payment,default控制消费顺序
任务失败了,日志在哪?怎么重试?
失败任务不会自动消失,Laravel 提供了内置追踪路径,但需主动启用:
- 先运行
php artisan queue:failed-table+php artisan migrate,生成failed_jobs表,否则失败记录无处可存 - 任务类中可定义
$tries = 3或public function retryUntil() { return now()->addMinutes(10); },比命令行--tries更精准 - 查失败记录用
php artisan queue:failed,重试某条用php artisan queue:retry 123(ID 来自上条命令输出),清空用php artisan queue:flush
最容易被忽略的点:数据库驱动下,jobs.payload 是 JSON 字段,如果任务携带大量数据(比如整个 Eloquent 模型),可能突破 MySQL 的 max_allowed_packet 限制,导致插入失败且无明确报错——此时应只传 ID,在 handle() 中重新查询。Redis 驱动虽无此限制,但 payload 过大会拖慢反序列化速度,同样要节制。











