任務調度
任務排程
- 簡介
- #定義調度
- Artisan 指令調度
- 佇列任務調度
- Shell 命令調度
- 調度頻率設定
- #時區
- 避免任務重複
- #任務只運行在一台伺服器上
- #後台任務
- 維護模式
- #任務輸出
#簡介
過去,你可能需要在伺服器上為每一個調度任務去建立Cron 入口。但這種方式很快就會變得不友好,因為這些任務調度不在原始程式碼中,而且你每次都需要透過 SSH 連結登入伺服器中才能增加 Cron 入口。
的
schedule方法中進行定義。為了幫助你更好的入門,這個方法有一個簡單的例子。
啟動調度器當使用這個調度器時,你只需要把下面的 Cron 入口加入到你的伺服器中即可。如果你不知道怎麼在伺服器中加入Cron 入口,可以考慮使用一些服務來管理Cron 入口,例如
:* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
這個Cron 為每分鐘執行一次Laravel 的命令列調度器。當
定義調度
你可以在App\Console\Kernel 類別的
schedule 方法中定義所有的排程任務。在開始之前,讓我們來看一個例子。在這個例子中,我們計劃每天午夜呼叫一個閉包。在閉包中,我們執行一個資料庫查詢來清空一張表:<?php
namespace App\Console;
use Illuminate\Support\Facades\DB;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel{
/**
* 应用里的自定义 Artisan 命令
*
* @var array
*/
protected $commands = [
//
];
/**
* 定义计划任务
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
DB::table('recent_users')->delete();
})->daily();
}
}
除了使用閉包來定義任務調度外,你也可以用
$schedule->call(new DeleteRecentUsers)->daily();###################
Artisan 指令調度
除了使用呼叫閉包這種方式來調度外,你還可以呼叫 Artisan 指令 和作業系統指令。例如,你可以給 command
方法傳遞指令名稱或類別來調度一個 Artisan 指令。
$schedule->command('emails:send --force')->daily(); $schedule->command(EmailsCommand::class, ['--force'])->daily();
佇列任務排程
job
方法可以用來調度 佇列任務。此方法提供了一種快速的方式來調度任務,而無需使用 call
方法建立閉包來調度任務。
$schedule->job(new Heartbeat)->everyFiveMinutes(); // 分发任务到「heartbeats」队列... $schedule->job(new Heartbeat, 'heartbeats')->everyFiveMinutes();
Shell 命令調度
exec
方法可用來向作業系統發送命令:
$schedule->exec('node /home/forge/script.js')->daily();#
調度頻率設定
當然了,你可以為你的任務分配多種排程:
方法 | 描述 |
---|---|
#->cron('* * * * *'); | 自訂Cron 時間表執行任務 |
#->everyMinute(); | #每分鐘執行一次任務 |
->everyFiveMinutes(); | 每五分鐘執行一次任務 |
| |
| |
| |
| |
#每十分鐘執行一次任務 | ##->everyFifteenMinutes (); |
每十五分鐘執行一次任務
| ->everyThirtyMinutes(); |
#每三十分鐘執行一次任務
| ->hourly(); |
每小時執行一次任務
| #->hourlyAt(17); |
| ##-> ;daily();|
| ->dailyAt('13: 00');|
->monthly(); | 每月執行一次任務 |
->monthlyOn( 4, '15:00'); | 每月4 號的15 點執行一次任務 |
##->quarterly();
| 每季執行一次任務|
->yearly();
| 每年執行一次任務|
->timezone('America/New_York');
| 設定時區
方法 | 描述 |
---|---|
->weekdays(); | 限制任務在工作日執行 |
->weekends(); | 限制任務在週末執行 |
->sundays(); | 限制任務在星期日執行 |
->mondays(); | 限制任務在週一執行 |
->tuesdays(); | #限制任務在星期二執行 |
## ->wednesdays();
| 限制任務在星期三執行|
->thursdays();
| |
| |
| |
| ->fridays(); 限制任務在星期五執行
|
限制任務在星期六執行 | |
限制任務在 | $start和 | $end
時間範圍限制
使用between
來限制任務在一天中的某個時間段來執行:
$schedule->command('reminders:send') ->hourly() ->between('7:00', '22:00');
或使用unlessBetween
方法來為任務排除一個時間段:
$schedule->command('reminders:send') ->hourly() ->unlessBetween('23:00', '4:00');
#閉包測試限制
使用when
方法來根據測試結果來執行任務。也就是說,如果給定的閉包回傳結果為true
,只要沒有其他約束條件阻止任務運行,任務就會一直執行下去:
$schedule->command('emails:send')->daily()->when(function () { return true; });
skip
方法可以看做是when
方法的逆過程。如果skip
方法回傳true
,任務就不會執行:
$schedule->command('emails:send')->daily()->skip(function () { return true; });
使用鍊式呼叫when
方法時,只有所有的when
都回傳true
時,任務才會執行。
環境約束
environments
方法可用於僅在給定環境中執行任務:
$schedule->command('emails:send') ->daily() ->environments(['staging', 'production']);
#時區
使用timezone
方法,你可以指定任務在給定的時區內執行:
$schedule->command('report:generate') ->timezone('America/New_York') ->at('02:00')
如果要為所有計劃任務分配相同的時區,則可能希望在app/Console/Kernel.php
檔案中定義scheduleTimezone
方法。此方法應傳回應指派給所有排程任務的預設時區:
/** * 获取默认情况下应为预定事件使用的时区。 * * @return \DateTimeZone|string|null */ protected function scheduleTimezone(){ return 'America/Chicago'; }
{註} 請記住,有些時區會使用夏令時間。當夏時制時間改變時,你的任務可能會執行兩次,甚至根本不會執行。所以我們建議盡量避免使用時區來安排計畫任務。
避免任務重複
預設情況下,即使先前的任務還在執行,調度內任務也會執行。你可以使用withoutOverlapping
方法來避免這種情況:
$schedule->command('emails:send')->withoutOverlapping();
在這個範例中,如果emails:send
Artisan 指令沒有正在運行,它將每分鐘執行一次。如果你的任務執行時間不確定,而且你又無法準確預估出任務的執行時間,那麼 withoutOverlapping
方法會顯得特別有用。
如果有需要,你可以指定「without overlapping」鎖定指定的時間範圍。預設情況下,鎖將在 24 小時後過期。
$schedule->command('emails:send')->withoutOverlapping(10);#
任務只運行在一台伺服器上
{note} 要使用這個特性,你的應用程式預設快取驅動必須是
memcached
或redis
。除此之外,所有的伺服器必須使用同一個中央快取伺服器通訊。
如果你的應用程式在多個伺服器上運行,你可能需要限制你的調度任務只在單一伺服器上運行。假設你有一個調度任務:每週五晚產生一份新報告。如果這個任務調度器在三個伺服器上運行,那麼這個任務會在三台伺服器上運行且產生三份報告。這樣不好!
為了說明任務應該在單一伺服器上執行,在定義排程任務時使用 onOneServer
方法。第一個取得到任務的伺服器會產生一個原子鎖,用來防止其他伺服器在同一時刻執行相同任務。
$schedule->command('report:generate') ->fridays() ->at('17:00') ->onOneServer();
後台任務
預設情況下,同時調度的多個命令會依序執行。如果你有長時間運行的命令,這可能會導致後續命令的啟動時間比預期的要晚。因此,你想要在後台同時執行指令,可以使用runInBackground
方法:
$schedule->command('analytics:report') ->daily() ->runInBackground();##維護模式Laravel 的佇列任務在維護模式下不會運作。因為我們不想你的調度任務幹擾到你伺服器上可能還未完成的專案。不過,如果你確實是想在維護模式下強制調度任務執行,你可以使用
evenInMaintenanceMode 方法:
$schedule->command('emails:send')->evenInMaintenanceMode();
任務輸出
Laravel 調度器提供了一些方便的方法來處理調度任務輸出。首先,你可以使用sendOutputTo
方法來輸出到文件以便於後續檢查:$schedule->command('emails:send') ->daily() ->sendOutputTo($filePath);
附加
到給定文件,可以使用 appendOutputTo 方法
$schedule->command('emails:send') ->daily() ->appendOutputTo($filePath);
使用
emailOutputTo# 方法,你可以將輸出傳送到指定郵箱。在使用郵件發送之前,你需要設定Laravel 的郵件服務:$schedule->command('foo') ->daily() ->sendOutputTo($filePath) ->emailOutputTo('foo@example.com');
$schedule->command('emails:send') ->daily() ->before(function () { // Task is about to start... }) ->after(function () { // Task is complete... });#######
Ping 網址
使用 pingBefore
和 thenPing
方法,你可以在任務執行前或執行後來 ping 指定的 URL。這個方法在通知外部服務(例如Laravel Envoyer)時將會特別有用:
$schedule->command('emails:send') ->daily() ->pingBefore($url) ->thenPing($url);
只有在給定條件為true
時,才能使用 pingBeforeIf
和thenPingIf
方法ping 指定的URL:
$schedule->command('emails:send') ->daily() ->pingBeforeIf($condition, $url) ->thenPingIf($condition, $url);
所有ping 方法都需要Guzzle HTTP 函式庫。你可以使用 Composer 來新增 Guzzle 到你的專案中:
composer require guzzlehttp/guzzle