队列任务通过php artisan make:job生成标准类,需实现shouldqueue接口;分发时调用dispatch()将其序列化入队,由queue:work进程消费执行handle()方法。

队列任务不是“创建”出来的,而是通过 php artisan make:job 命令生成一个可被分发(dispatch())的类;真正起作用的是你把它放进队列、由 queue:work 进程消费的过程。
怎么生成一个队列任务类
用 Artisan 命令快速生成标准结构的任务类,它默认实现 ShouldQueue 接口,表示该任务会被推入队列而非同步执行:
php artisan make:job SendWelcomeEmail
生成的 SendWelcomeEmail 类位于 app/Jobs/SendWelcomeEmail.php,内部已包含 handle() 方法入口。注意:Laravel 9+ 默认不自动加 implements ShouldQueue,需手动补上(否则会同步执行)。
- 如果想让任务延迟执行,直接在
dispatch()时链式调用delay(),比如SendWelcomeEmail::dispatch($user)->delay(now()->addMinutes(5)) - 不要在构造函数里做重操作或依赖未序列化的对象(如
Request、UploadedFile),因为任务会被序列化后存入 Redis/DB,反序列化时会失败 - 数据库驱动下,
jobs表的payload字段是 JSON,过长可能触发 MySQL 的max_allowed_packet限制
怎么把任务真正扔进队列
生成类只是第一步,关键在「分发」——调用 dispatch() 静态方法(来自 Dispatchable trait):
SendWelcomeEmail::dispatch($user);
这行代码不会立刻执行 handle(),而是将任务序列化后写入配置的队列驱动(如 Redis 或 database)。常见误区:
- 误以为
new SendWelcomeEmail($user)->handle()是走队列——这是同步调用,完全绕过队列系统 - 环境配置没切到队列模式:
QUEUE_CONNECTION必须设为redis、database等非sync值;sync是默认值,只用于本地调试,它根本不会进队列 - 使用
dispatchNow()会强制同步执行,适合单元测试,但别在生产逻辑里混用
为什么 queue:work 没反应或报错
运行 php artisan queue:work 后无日志、无消费,大概率是驱动或连接问题:
- 检查
.env中QUEUE_CONNECTION=redis(或database),且对应服务(Redis 或 DB)可连通;用php artisan tinker执行Redis::ping()或DB::connection()->getPdo()快速验证 - Redis 驱动下,若提示
Connection refused,确认REDIS_HOST和REDIS_PORT正确,且 Laravel 使用的 Redis 数据库索引(REDIS_DB)没被其他进程清空 - database 驱动下,确保
jobs表存在(运行php artisan queue:table && php artisan migrate),且failed_jobs表也已建好(否则失败任务无法记录) -
queue:work默认只处理一个队列(default),若任务指定了其他队列名(如SendWelcomeEmail::dispatch($user)->onQueue('emails')),要显式指定:php artisan queue:work --queue=emails
任务失败后怎么重试和清理
Laravel 自动处理失败任务,但行为取决于配置和驱动:
- 默认最多尝试 3 次(由
tries属性控制),超限后进failed_jobs表;可在任务类里设public $tries = 5; - 用
php artisan queue:failed查看失败列表,用php artisan queue:retry {id}重试单个,或php artisan queue:flush清空整个失败表(慎用) - database 驱动下,失败任务不会自动从
jobs表删除,而是移到failed_jobs;Redis 驱动则完全不保留失败快照,只能靠日志排查 - 如果任务含外部 HTTP 请求,记得加
try/catch并主动throw异常——否则静默失败,queue:work不会重试
队列不是黑盒,它的可靠性高度依赖驱动稳定性、序列化安全性和失败策略设计;别只盯着 make:job,重点盯住 dispatch() 调用点、QUEUE_CONNECTION 环境值、以及 queue:work 进程是否真实在跑——这三个地方出错,任务就永远卡在“以为进了队列”的幻觉里。











