dispatch() 默认同步执行是因为 QUEUE_CONNECTION 配置为 sync;需检查 config('queue.default') 和 .env 中 QUEUE_CONNECTION 是否设为 redis 等异步驱动。

为什么 dispatch() 有时不进队列而是直接同步执行
默认情况下,Laravel 的 dispatch() 会根据当前 QUEUE_CONNECTION 配置决定是否异步。如果环境变量里没设或设成了 sync,任务就立刻执行,根本不会进 Redis/RabbitMQ —— 这不是 bug,是 Laravel 的“同步驱动”设计使然。
检查方式:运行 php artisan tinker 后输入 config('queue.default'),确认返回值不是 sync;再看 .env 是否有 QUEUE_CONNECTION=redis(或 database、beanstalkd 等)。
- 本地开发想快速验证队列行为?把
.env改成QUEUE_CONNECTION=redis,并确保redis-server已启动 - 线上环境用
database驱动时,别忘了运行php artisan queue:table+php artisan migrate - 使用
dispatchNow()是明确要跳过队列的主动选择,不是配置失效的征兆
如何让一个普通函数也能进队列(非 Job 类)
Laravel 不允许直接对闭包或普通函数调用 dispatch(),但可以用 Bus::dispatch() 包一层匿名任务,或者更稳妥地——封装成可序列化的 class@method 形式。
推荐做法是写一个轻量 Job 类,哪怕只有一行逻辑。例如:
立即学习“PHP免费学习笔记(深入)”;
php artisan make:job ProcessUserReport
然后在 handle() 里调用你的辅助函数,比如 $this->reportService->generate($this->userId)。这样既可被序列化,又方便测试和重试。
- 别在 Job 构造函数里执行耗时操作,所有逻辑放
handle()中 - 如果必须传对象,确保它实现了
__serialize()/__unserialize(),或改用 ID 等标量参数 - 闭包无法被 PHP 序列化,
dispatch(function () { ... })在 Laravel 9+ 会直接报Serialization of 'Closure' is not allowed
delay() 和 onQueue() 为什么有时不生效
这两个链式方法必须放在 dispatch() 调用**之前**,且只能作用于实现了 ShouldQueue 接口的 Job 类。如果写成 MyJob::dispatch()->delay(...),PHP 会先实例化 Job 并执行构造逻辑,此时 delay() 已失去控制权。
正确写法是:
MyJob::dispatch($userId)->delay(now()->addMinutes(5))->onQueue('reports');
-
delay()接收\DateTimeInterface或秒数(如300),不是字符串'5 minutes' -
onQueue()指定的是队列名(如redis配置下的default、emails),不是连接名;连接由QUEUE_CONNECTION决定 - 数据库驱动下,
delay字段存的是时间戳,若服务器时间不同步,会导致延迟不准
队列监听进程挂了怎么办:重启策略与日志定位
php artisan queue:work 是单进程,遇到内存溢出、未捕获异常或信号中断就会退出,表现为“任务积压但 worker 不消费”。不能靠 crontab 每分钟拉一次,要用进程管理工具保活。
生产环境务必用 supervisor 或 systemd 管理 queue:work,并配置 autostart=true、autorestart=true、startretries=3。
- 日志路径默认是
storage/logs/laravel.log,搜索关键词"failed"或"exception"能快速定位崩溃原因 - 加
--max-jobs=1000或--max-time=3600防止单个 worker 占用太久内存 - 修改代码后,worker 不会自动 reload,必须手动
supervisorctl restart laravel-worker
队列不是“设了就能跑”,它依赖配置、驱动状态、进程管理、错误隔离四层联动。漏掉任意一环,都可能让任务静默失败。



















