Laravel 13 任务调度详解
任务调度是自动化后台任务的核心功能。Laravel 13 提供了优雅的任务调度系统,可以替代传统的 Cron 配置。
定义调度
调度文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel { protected function schedule(Schedule $schedule): void { $schedule->command('inspire') ->hourly(); }
protected function commands(): void { $this->load(__DIR__.'/Commands'); } }
|
调度命令
基本调度
1 2 3 4 5 6 7 8
| use Illuminate\Console\Scheduling\Schedule;
protected function schedule(Schedule $schedule): void { $schedule->command('emails:send')->daily(); $schedule->command('emails:send --force')->daily(); $schedule->command(EmailsSendCommand::class, ['--force'])->daily(); }
|
调度 Artisan 命令
1 2 3
| $schedule->command('cache:clear')->daily(); $schedule->command('queue:work --daemon')->everyMinute(); $schedule->command('backup:run')->dailyAt('02:00');
|
调度频率
常用频率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| $schedule->command('task')->everyMinute(); $schedule->command('task')->everyTwoMinutes(); $schedule->command('task')->everyThreeMinutes(); $schedule->command('task')->everyFourMinutes(); $schedule->command('task')->everyFiveMinutes(); $schedule->command('task')->everyTenMinutes(); $schedule->command('task')->everyFifteenMinutes(); $schedule->command('task')->everyThirtyMinutes();
$schedule->command('task')->hourly(); $schedule->command('task')->hourlyAt(17); $schedule->command('task')->everyOddHour();
$schedule->command('task')->daily(); $schedule->command('task')->dailyAt('13:00'); $schedule->command('task')->twiceDaily(1, 13); $schedule->command('task')->twiceDailyAt(1, 13, 15);
$schedule->command('task')->weekly(); $schedule->command('task')->weeklyOn(1, '08:00');
$schedule->command('task')->monthly(); $schedule->command('task')->monthlyOn(4, '15:00'); $schedule->command('task')->twiceMonthly(1, 16, '13:00'); $schedule->command('task')->lastDayOfMonth('15:00');
$schedule->command('task')->quarterly(); $schedule->command('task')->yearly();
$schedule->command('task')->cron('* * * * *');
|
工作日调度
1 2 3 4 5 6 7 8 9
| $schedule->command('task')->weekdays(); $schedule->command('task')->weekends(); $schedule->command('task')->mondays(); $schedule->command('task')->tuesdays(); $schedule->command('task')->wednesdays(); $schedule->command('task')->thursdays(); $schedule->command('task')->fridays(); $schedule->command('task')->saturdays(); $schedule->command('task')->sundays();
|
时间约束
1 2 3 4 5 6 7 8 9 10 11
| $schedule->command('task') ->daily() ->between('08:00', '17:00');
$schedule->command('task') ->daily() ->unlessBetween('12:00', '13:00');
$schedule->command('task') ->daily() ->days([1, 15]);
|
调度闭包
1 2 3 4 5 6 7
| $schedule->call(function () { User::where('expired', true)->delete(); })->daily();
$schedule->call(function () { Cache::flush(); })->name('clear-cache')->daily();
|
调度 Shell 命令
1 2 3 4 5
| $schedule->exec('node /home/forge/script.js')->daily();
$schedule->exec('/usr/bin/npm run build') ->daily() ->path('/path/to/project');
|
调度任务
1 2 3 4 5
| use App\Jobs\ProcessPodcast;
$schedule->job(new ProcessPodcast('podcast-id'))->hourly();
$schedule->job(new ProcessPodcast('podcast-id'), 'podcasts')->hourly();
|
调度条件
环境条件
1 2 3
| $schedule->command('task') ->daily() ->environments(['production', 'staging']);
|
维护模式
1 2 3
| $schedule->command('task') ->daily() ->evenInMaintenanceMode();
|
条件回调
1 2 3 4 5 6 7 8 9 10 11
| $schedule->command('task') ->daily() ->when(function () { return true; });
$schedule->command('task') ->daily() ->skip(function () { return true; });
|
防止重叠
1 2 3 4 5 6 7 8 9 10 11 12
| $schedule->command('task') ->daily() ->withoutOverlapping();
$schedule->command('task') ->daily() ->withoutOverlapping(10);
$schedule->command('task') ->daily() ->withoutOverlapping() ->onOneServer();
|
单服务器运行
1 2 3
| $schedule->command('task') ->daily() ->onOneServer();
|
后台运行
1 2 3
| $schedule->command('task') ->daily() ->runInBackground();
|
输出处理
发送输出到文件
1 2 3 4 5 6 7
| $schedule->command('task') ->daily() ->sendOutputTo($filePath);
$schedule->command('task') ->daily() ->appendOutputTo($filePath);
|
发送输出到邮箱
1 2 3 4 5 6 7
| $schedule->command('task') ->daily() ->emailOutputTo('admin@example.com');
$schedule->command('task') ->daily() ->emailOutputOnFailure('admin@example.com');
|
钩子
前置和后置钩子
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| $schedule->command('task') ->daily() ->before(function () { }) ->after(function () { }) ->onSuccess(function () { }) ->onFailure(function () { });
|
任务输出
输出到存储
1 2 3 4 5
| use Illuminate\Support\Facades\Storage;
$schedule->command('report:generate') ->daily() ->storeOutputIn('reports/daily.log');
|
监控任务
监控任务运行
1 2 3 4 5 6 7 8 9
| $schedule->command('task') ->daily() ->pingBefore($url) ->thenPing($url);
$schedule->command('task') ->daily() ->pingBeforeIf($condition, $url) ->thenPingIf($condition, $url);
|
启动调度器
Cron 配置
1
| * * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
|
手动运行
1 2 3 4
| php artisan schedule:run php artisan schedule:run --verbose php artisan schedule:test php artisan schedule:list
|
调度示例
完整示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| protected function schedule(Schedule $schedule): void { $schedule->command('backup:run') ->daily() ->at('02:00') ->onOneServer() ->withoutOverlapping() ->emailOutputOnFailure('admin@example.com');
$schedule->command('analytics:aggregate') ->hourly() ->between('00:00', '06:00') ->environments(['production']);
$schedule->command('subscriptions:expire') ->daily() ->when(fn () => config('subscriptions.enabled'));
$schedule->job(new ProcessPendingOrders) ->everyFiveMinutes() ->withoutOverlapping() ->onOneServer();
$schedule->call(function () { Cache::put('last_cleanup', now()); User::where('last_activity', '<', now()->subDays(30))->delete(); }) ->daily() ->name('cleanup-inactive-users');
$schedule->command('report:weekly') ->weekly() ->mondays() ->at('08:00') ->emailOutputTo(['manager@example.com', 'admin@example.com']);
$schedule->exec('node /path/to/script.js') ->daily() ->path('/path/to/project') ->runInBackground(); }
|
最佳实践
1. 使用 onOneServer
1 2 3 4
| $schedule->command('task') ->daily() ->onOneServer();
|
2. 防止重叠
1 2 3 4
| $schedule->command('task') ->everyMinute() ->withoutOverlapping();
|
3. 使用任务钩子
1 2 3 4 5 6 7 8
| $schedule->command('backup:run') ->daily() ->onSuccess(function () { Log::info('Backup completed'); }) ->onFailure(function () { Log::error('Backup failed'); });
|
4. 合理设置频率
1 2 3 4 5 6 7
| $schedule->command('cache:clear')->daily(); $schedule->command('queue:work')->everyMinute(); $schedule->command('backup:run')->daily();
$schedule->command('backup:run')->everyMinute();
|
总结
Laravel 13 的任务调度系统提供了强大而灵活的后台任务自动化能力。通过合理使用调度频率、条件约束、防止重叠和钩子功能,可以构建出可靠的后台任务系统。记住使用 onOneServer 防止多服务器重复执行、使用 withoutOverlapping 防止任务重叠、并充分利用钩子来监控任务状态。任务调度是自动化运维的核心,掌握它对于构建高效的应用程序至关重要。