Laravel调度器需系统cron每分钟调用才能生效,直接运行php artisan schedule:run仅执行当前到期任务;闭包任务不支持队列,须改用自定义命令;withoutOverlapping()多机部署时需Redis共享锁;日志需手动配置输出路径并添加业务日志。

为什么 php artisan schedule:run 没反应?
因为 Laravel 的调度器本身不启动进程,它只是个“任务分发器”,必须靠系统级定时器(如 Linux cron)每分钟调用一次才能生效。本地直接跑 php artisan schedule:run 只会执行当前已到时间的任务,不会持续监听。
实操建议:
- 在服务器上运行
crontab -e,添加一行:* * * * * cd /var/www/your-app && php artisan schedule:run >> /dev/null 2>&1 - 路径必须写绝对路径,
cd切到项目根目录再执行,否则artisan可能读不到配置或加载不到服务提供者 - 别用
sudo crontab -e,要用部署应用的用户(比如www-data或deploy)来设 cron,否则日志、文件权限、队列连接都可能出错
如何让 schedule() 里的闭包任务支持队列?
Laravel 默认把闭包任务当作同步执行,即使你写了 ->onQueue('high'),也不会进队列——闭包无法被序列化投递到 Redis 或 database 驱动里。
实操建议:
- 改用自定义命令:运行
php artisan make:command SendDailyReport,在handle()里写业务逻辑 - 在
App\Console\Kernel.php的schedule()方法中注册:$schedule->command('report:daily')->dailyAt('09:00')->onQueue('reports') - 确保
SendDailyReport命令类继承Illuminate\Console\Command,且没手动调用$this->dispatchNow()—— 否则绕过队列直执行
withoutOverlapping() 在多台服务器部署时失效?
这个方法依赖本地缓存(默认是 file 或 array),只锁当前机器上的进程。如果你用的是多实例负载均衡(比如 3 台 PHP-FPM 服务器),每台都会各自判断“没重叠”,结果任务被重复执行三次。
实操建议:
- 切换缓存驱动为共享存储,比如 Redis:
Cache::store('redis')->put(...),并确保config/cache.php中default是redis - 或者显式指定锁驱动:
$schedule->command('backup:db')->hourly()->withoutOverlapping(3600, 'redis'),第二个参数是 store 名称 - 注意:如果 Redis 不可用,
withoutOverlapping()会静默失败,任务照常运行——得配好健康检查和告警
日志写到哪了?怎么查某次调度是否真的触发?
Laravel 调度器本身不记录日志,schedule:run 的输出默认被丢进 /dev/null。你看到“没报错”,不等于任务执行成功;可能连命令都没找到,或抛了异常后被吞掉。
实操建议:
- 临时调试时,在 cron 行末尾去掉
>> /dev/null 2>&1,改用>> /var/log/laravel-schedule.log 2>&1 - 在每个任务命令的
handle()开头加Log::info('report:daily started at ' . now());,确保日志通道(比如 daily file)已启用 - 检查
storage/logs/laravel.log里有没有类似Running scheduled command: App\Console\Commands\SendDailyReport的记录——这是schedule:run输出的,不是你的业务日志
复杂点在于:调度逻辑、命令执行、队列投递、队列消费,四层环节各自有失败可能,但只有最外层(cron → schedule:run)的日志是统一入口。漏掉任意一层的错误捕获,问题就藏得深。


















