Laravel用Redis做队列需确保三处正确:驱动配置指向真实Redis实例、任务分发传模型ID而非实例、worker必须用supervisor守护;否则易静默失败。

直接说结论:Laravel 用 Redis 做队列,不是配对就能跑通,关键在三处——驱动配置必须指向真实 Redis 实例、任务分发时别传 Eloquent 模型实例、worker 必须用 supervisor 长期守护,否则一关终端就停。
确认 Redis 连接是否真通,别被 .env 假象骗了
很多人改了 QUEUE_CONNECTION=redis 就以为完事,但 Laravel 会默认用 config/database.php 里 redis.default 的配置。如果本地装了 Redis 却没启动,或生产环境 Redis 地址/密码写错,dispatch() 看似成功,其实任务根本没进队列——连失败日志都不报,静默丢弃。
- 先手动测试连接:
php artisan tinker→Redis::ping(),返回"PONG"才算通 - 检查
config/queue.php中redis连接块的connection键是否和database.redis下实际定义的名字一致(比如你配的是cache,但 queue 里写default,就断开) - 如果用 PhpRedis 扩展(非 Predis),要确保
php -m | grep redis能看到redis,且config/queue.php中没硬编码依赖Predis
dispatch() 时传模型 ID,而不是模型对象本身
这是最常导致内存溢出或序列化失败的坑。Eloquent 模型带大量关联属性、访问器、原始查询状态,serialize() 它进 Redis 不仅慢,还可能因闭包、资源句柄等直接报错 Serialization of 'Closure' is not allowed。
- 错误写法:
SendEmailJob::dispatch($user)($user 是完整模型实例) - 正确写法:
SendEmailJob::dispatch($user->id),然后在handle()里重新查:$user = User::findOrFail($this->userId) - 如果实在要传模型,加
use SerializesModels;特质,并确保模型没加载太多关系(load()过的会全序列化)
queue:work 不能裸跑,必须用 supervisor 管着
php artisan queue:work 是个前台命令,关掉 SSH 就退出;哪怕加 --daemon,也扛不住内存泄漏、超时卡死、PHP 扩展崩溃——它不自动重启,任务就堆在队列里不动。
- Supervisor 配置里必须设
autostart=true、autorestart=true、startsecs=3,否则进程挂了没人拉起来 - 推荐加参数限制单个 worker 行为:
php artisan queue:work --timeout=60 --tries=3 --max-jobs=100,防止单任务吃光内存或无限重试 - 别用
queue:listen,它每次执行都 reload 整个 Laravel 应用,比queue:work慢 3–5 倍,只适合调试
延迟任务和指定队列名,得看场景选对方法
不是所有“稍后执行”都该用 delay()。Redis 队列原生不支持精确延时(靠轮询),高并发下会有秒级偏差;而分队列(如 reports、notifications)是真正解耦的关键。
- 简单延迟(如 5 分钟后发提醒):
SendEmailJob::dispatch($id)->delay(now()->addMinutes(5)) - 业务隔离(报表导出不能挤占邮件发送):
SendEmailJob::dispatch($id)->onQueue('notifications'),再单独起 worker:php artisan queue:work --queue=notifications - 慎用
afterCommit():只有事务提交后才入队,适合强一致性场景,但会增加请求响应时间——它等的是 DB commit,不是 HTTP 响应
Redis 队列看着简单,但模型传递、连接验证、进程守护这三环只要漏一个,上线后就是静默失败+用户投诉。别信“配完就能用”,上线前一定用 failed_jobs 表查一遍有没有积压,用 redis-cli monitor 看一眼任务是否真进了 queues:default key。










