1. 程式人生 > 實用技巧 >Laravel入坑指南(番外)——任務排程

Laravel入坑指南(番外)——任務排程

Laravel提供了非常強勁的命令列工具(如果還不瞭解,傳送到第8往篇),我們如果想要定期執行某個命令列,可以利用crontab進行定時設定。如果有多個定期的任務,很簡單,我們設定多條crontab規則在不同時間段內排程不同的命令即可。

而Laravel框架內部也提供了這樣的排程機制。(當然,還是逃不過crontab的支援)

首先,我們需要在crontab中新增這麼一條規則

* * * * * php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1

其次,所有的排程任務都在/app/Console/Kernel.php的schedule函式中進行排程設定

這個函式中可以排程閉包、可呼叫物件(包含__invoke的物件)、artisian命令、列隊任務以及shell命令。以下以例子進行說明:

//排程閉包,->daily()即排程選項,每天凌晨零點執行任務
//關於排程選項後面會統一說明
$schedule->call(function () {
    DB::table('recent_users')->delete();
})->daily();


//排程可呼叫物件
//DeleteRecentUsers類中包含了__invoke魔術方法
$schedule->call(new DeleteRecentUsers)->daily();


//排程artisian命令 $schedule->command('emails:send --force')->daily(); $schedule->command(EmailsCommand::class, ['--force'])->daily(); //排程列隊任務 $schedule->job(new Heartbeat)->everyFiveMinutes(); $schedule->job(new Heartbeat, 'heartbeats')->everyFiveMinutes(); //將job投遞到heartbeats列隊中 //排程shell命令
$schedule->exec('node /home/forge/script.js')->daily();

常用的排程選項:

方法 描述
->cron('* * * * *'); 在自定義Cron排程上執行任務
->everyMinute(); 每分鐘執行一次任務
->everyFiveMinutes(); 每五分鐘執行一次任務
->everyTenMinutes(); 每十分鐘執行一次任務
->everyFifteenMinutes(); 每十五分鐘執行一次任務
->everyThirtyMinutes(); 每三十分鐘執行一次任務
->hourly(); 每小時執行一次任務
->hourlyAt(17); 每小時第十七分鐘執行一次任務
->daily(); 每天凌晨零點執行任務
->dailyAt('13:00'); 每天13:00執行任務
->twiceDaily(1, 13); 每天1:00 & 13:00執行任務
->weekly(); 每週執行一次任務
->monthly(); 每月執行一次任務
->monthlyOn(4, '15:00'); 每月4號15:00執行一次任務
->quarterly(); 每個季度執行一次
->yearly(); 每年執行一次
->timezone('America/New_York'); 設定時區

額外排程約束選項:

方法 描述
->weekdays(); 只在工作日執行任務
->weekends(); 只在週末執行任務
->sundays(); 每個星期天執行任務
->mondays(); 每個星期一執行任務
->tuesdays(); 每個星期二執行任務
->wednesdays(); 每個星期三執行任務
->thursdays(); 每個星期四執行任務
->fridays(); 每個星期五執行任務
->saturdays(); 每個星期六執行任務
->between($start, $end); 基於特定時間段執行任務
->when(Closure); 基於特定測試執行任務
->environments($env); 只在指定環境執行任務

舉幾個例子:

// 每週星期一13:00執行一次...
$schedule->call(function () {
    
})->weekly()->mondays()->at('13:00');


// 工作日的上午8點到下午5點每小時執行...
$schedule->command('foo')
         ->weekdays()
         ->hourly()
         ->timezone('America/Chicago')
         ->between('8:00', '17:00');


//每天7點到22點之間,每小時執行
$schedule->command('reminders:send')
         ->hourly()
         ->between('7:00', '22:00');


//每天23點到4點之外的時間內,每小時執行
$schedule->command('reminders:send')
         ->hourly()
         ->unlessBetween('23:00', '4:00');


//when的閉包返回true時執行
$schedule->command('emails:send')->daily()->when(function () {
    return true;
});


//skip的閉包返回false時執行
$schedule->command('emails:send')->daily()->skip(function () {
    return true;
});

第三,任務排程的重疊處理

當下一次任務開始執行時,萬一上一次的任務還沒有執行完成,新的任務是會執行的。也就是說,有可能出現多次任務重疊執行的情況。

為了避免這種情況,我們可以使用外部的手段來加鎖,比如檔案鎖flock,或者redis的setnx都可以。但是Laravel非常貼心,已經幫我們準備了這樣的機制:

$schedule->command('emails:send')->withoutOverlapping();    //鎖預設24小時失效

$schedule->command('emails:send')->withoutOverlapping(10);    //鎖10分鐘失效

第四,任務排程的順序

預設情況下,同時排程的多個命令會順序執行。如果你有一些長時間執行的命令,將會導致隨後的命令在預期之後很久才能執行。如果你想要讓命令在後臺執行以便它們可以同時運,可以使用 runInBackground 方法來實現:

$schedule->command('analytics:report')
     ->daily()
     ->runInBackground();

第五,任務輸出

我們可以將任務中的輸出重定向覆蓋寫入或追加寫入檔案中,便於問題的排查:

//覆蓋寫入
$schedule->command('emails:send')
     ->daily()
     ->sendOutputTo($filePath);

//追加寫入
$schedule->command('emails:send')
     ->daily()
     ->appendOutputTo($filePath);

最後,任務鉤子

使用 before 和 after 方法,你可以指定在排程任務完成之前和之後要執行的程式碼:

$schedule->command('emails:send')
         ->daily()
         ->before(function () {
             // 任務即將開始...
         })
         ->after(function () {
             // 任務已經完成...
         });

好了,Laravel話題到這裡正式結束了。關於Laravel還有一些知識點,如果想要深入瞭解的小夥伴就去看官方文件吧。

下週開始,博主會進入另一個話題。至於要寫什麼,嗯,還沒想好,大家下週再見:)

--------------------------- 我是可愛的分割線 ----------------------------

最後博主借地宣傳一下,漳州程式設計小組招新了,這是一個面向漳州青少年資訊學/軟體設計的學習小組,有意向的同學點選連結,聯絡我吧。