在开发 Web 应用时,经常需要定时执行某些任务,比如每天清理临时文件、每小时同步数据、每分钟发送报告。传统做法是在服务器上添加 Cron 条目,但管理大量 Cron 命令会变得混乱。Laravel 提供了优雅的任务调度(Task Scheduling)功能,让你在代码中定义调度规则,只需在服务器上设置一个 Cron 入口,即可轻松管理所有定时任务。
routes/console.php),无需 SSH 到服务器维护 Cron,调度器支持丰富的频率设置、并行限制、输出处理等。
所有调度任务都在 routes/console.php 文件中定义。使用 Schedule facade 的 command、exec、call 等方法。
use Illuminate\Support\Facades\Schedule;
// 每天午夜执行一个 Artisan 命令
Schedule::command('emails:send')->daily();
// 每小时调用一次闭包
Schedule::call(function () {
DB::table('recent_users')->delete();
})->hourly();
// 每分钟执行一个 shell 命令
Schedule::exec('node /path/to/script.js')->everyMinute();
定义好调度后,需要在服务器上添加唯一的 Cron 条目,每分钟触发 Laravel 调度器:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
这样 Laravel 就会自动根据你的定义执行任务。
最常用的是调度自定义 Artisan 命令。使用 command() 方法传入命令签名。
Schedule::command('emails:send --force')->dailyAt('02:00');
// 也可以传入完整类名
Schedule::command(\App\Console\Commands\SendEmails::class)->mondays();
简单的逻辑可以直接用闭包,避免创建独立命令。但注意闭包内不要使用 dd() 或 exit,否则会中断调度器。
Schedule::call(function () {
$userCount = User::whereDate('created_at', today())->count();
Log::info("今日新增用户: {$userCount}");
})->dailyAt('23:50');
执行系统命令使用 exec(),可以捕获输出:
Schedule::exec('php artisan backup:run')
->daily()
->sendOutputTo(storage_path('logs/backup.log'));
Laravel 提供了大量方法设置任务执行频率,以下列出常用选项:
| 方法 | 说明 |
|---|---|
->cron('* * * * *') | 自定义 Cron 表达式 |
->everyMinute() | 每分钟执行 |
->everyTwoMinutes() | 每两分钟 |
->hourly() | 每小时整点 |
->hourlyAt(17) | 每小时的第 17 分钟 |
->daily() | 每天午夜 |
->dailyAt('13:00') | 每天 13:00 |
->twiceDaily(1, 13) | 每天 1:00 和 13:00 |
->weekly() | 每周日 0:00 |
->monthly() | 每月1号 0:00 |
->weekdays() | 仅工作日 |
->weekends() | 仅周末 |
->sundays() / ->mondays() 等 | 指定星期几 |
还可以组合约束条件,例如:
Schedule::command('reports:generate')
->dailyAt('23:00')
->between('22:00', '06:00')
->when(function () {
return date('D') !== 'Sat';
});
有些任务执行时间可能超过间隔时间(例如一个每分钟执行的任务运行了 2 分钟)。使用 withoutOverlapping() 可以确保同一任务不会重叠,Laravel 会使用应用缓存作为锁。
Schedule::command('emails:send')->everyMinute()->withoutOverlapping();
默认锁有效期 24 小时,可以自定义过期时间:
->withoutOverlapping(10) // 10 分钟后释放锁
当 Laravel 处于维护模式(php artisan down)时,默认所有调度任务都不会执行。如果你希望某些任务在维护模式下仍运行,可以调用 evenInMaintenanceMode():
Schedule::command('backup:run')->daily()->evenInMaintenanceMode();
你可以将任务的输出重定向到文件,或通过邮件发送。
// 追加到文件
Schedule::command('emails:send')
->daily()
->appendOutputTo(storage_path('logs/email.log'));
// 发送邮件给指定地址
Schedule::command('reports:generate')
->daily()
->emailOutputTo('admin@example.com');
// 仅当输出非空时才发送邮件
->emailOutputOnFailure('admin@example.com');
任务钩子允许在任务执行前后执行回调:
Schedule::command('backup:run')
->daily()
->before(function () {
Log::info('备份开始');
})
->after(function () {
Log::info('备份完成');
});
你可以使用 schedule:list 命令查看所有已定义的调度任务:
php artisan schedule:list
手动触发某个调度任务(便于测试):
php artisan schedule:run
使用 --verbose 查看详细输出。
你可以在 App\Console\Kernel 类的 schedule() 方法外使用 Schedule::before() 和 Schedule::after() 来定义所有任务的前置/后置操作。
// 在 App\Console\Kernel 的 schedule 方法中
protected function schedule(Schedule $schedule)
{
$schedule->command('emails:send')->daily();
$schedule->before(function () {
// 所有任务执行前
});
$schedule->after(function () {
// 所有任务执行后
});
}
* * * * * php artisan schedule:run,所有调度逻辑在代码中管理。withoutOverlapping()。base_path()、storage_path() 等辅助函数。emailOutputOnFailure() 或集成监控系统(如 Oh Dear, Healthchecks)感知任务失败。schedule:list 验证调度定义是否符合预期。evenInMaintenanceMode()。
// 在 routes/console.php 中
use Illuminate\Support\Facades\Schedule;
Schedule::command('backup:run')
->dailyAt('02:00')
->withoutOverlapping()
->emailOutputOnFailure('admin@example.com');
Schedule::call(function () {
$files = glob(storage_path('temp/*'));
foreach ($files as $file) {
if (is_file($file) && filemtime($file) < now()->subDays(7)->timestamp) {
unlink($file);
}
}
})->weekly()->sundays()->at('03:00');
Laravel 任务调度器将复杂的 Cron 管理简化为优雅、可读的 PHP 代码,极大地提高了定时任务的可维护性。通过掌握调度定义、频率选项、重叠控制、输出处理等特性,你可以轻松实现各种周期性任务,并确保它们在正确的时间以正确的方式运行。