laravel定时任务需同时配置代码调度逻辑和服务器cron:在kernel.php中用command()/job()/call()定义任务,在服务器添加绝对路径的 * /usr/bin/php /path/artisan schedule:run命令。

要让 Laravel 的定时任务真正跑起来,核心就两点:代码里定义清楚“做什么、什么时候做”,服务器上配好“谁来每分钟叫它一声”。光写 schedule() 方法不配 Cron,任务永远不会动;光配 Cron 不写调度逻辑,也只是空转。下面从实操角度拆解关键环节。
服务器必须加这一条 Cron 入口
Laravel 调度器不是后台服务,它靠系统级 Cron 每分钟拉起一次 php artisan schedule:run,再由框架内部判断该执行哪些任务。这条 Cron 必须加在服务器上:
- 用 crontab -e 编辑当前用户的定时任务列表
- 添加完整命令(路径和 PHP 可执行文件务必用绝对路径):
* * * * * /usr/bin/php /var/www/your-app/artisan schedule:run >> /dev/null 2>&1 - 如果项目不在标准路径,把 /var/www/your-app 换成你的真实项目根目录
- 确保 APP_ENV=production,开发环境默认跳过调度执行
- 改完后运行 sudo systemctl restart cron 或 service cron reload 生效
在 Kernel.php 中定义任务的三种常用方式
所有调度逻辑集中在 app/Console/Kernel.php 的 schedule() 方法里。推荐按任务性质选择写法:
-
用 command() 调 Artisan 命令:适合需要完整 Laravel 生命周期的任务,比如发邮件、导出数据、调队列
→ $schedule->command('emails:send --force')->dailyAt('8:00'); -
用 job() 调队列任务:适合耗时操作,自动进队列异步处理
→ $schedule->job(new SendDailyReport)->daily(); -
用 call() 执行闭包或可调用对象:适合轻量操作,如清缓存、打日志
→ $schedule->call(function () { cache()->forget('stats'); })->hourly();
注意:闭包不能访问 $this,也不能依赖未 use 的变量
Cron 表达式与链式方法别混用
Laravel 提供两类时间控制,语义和精度不同:
- 链式方法(如 ->daily()、->everyFiveMinutes()):由 Laravel 在每次 schedule:run 调用时判断是否到达执行点,实际窗口可能有 ±60 秒漂移
- cron() 字符串(如 ->cron('0 */2 * * *')):交由系统 Cron 控制,更准时,但需严格遵循语法(0 */2 表示每两小时整点,不是“每隔两小时”)
- 避免对同一任务既写 ->hourly() 又加 ->withoutOverlapping()——前者已按小时粒度触发,后者锁机制在此场景下冗余
多服务器部署时防止重复执行
当应用部署在多个节点(如负载均衡后),默认每个节点都会运行 schedule:run,导致任务重复。Laravel 默认的 withoutOverlapping() 基于本地文件锁,只适用于单机:
- 跨服务器必须用共享存储实现分布式锁,推荐 Redis:
$schedule->command('notify:daily')->everyMinute()->onOneServer(); - onOneServer() 是 Laravel 内置方案,依赖缓存驱动(需配置为 redis 或 database)
- 若用自定义锁逻辑,可在任务开始前手动获取 Redis 锁,失败则直接 return
- 关键业务任务建议收口到一台固定机器执行,其他节点 Cron 不启用调度
调试与验证技巧
别等任务到了时间才看结果,这些命令能快速验证逻辑是否生效:
- 手动触发并查看匹配项:php artisan schedule:run --verbose
- 列出所有注册任务及下次运行时间:php artisan schedule:list
- 本地开发可用常驻监听模式(仅调试用):php artisan schedule:work
- 调试阶段把输出重定向到日志文件,而不是静默丢弃:
php artisan schedule:run > /var/log/laravel-schedule.log 2>&1 - 检查 config/app.php 中的 'timezone' 是否设为 IANA 格式(如 'Asia/Shanghai'),它直接影响 dailyAt() 等方法的计算











