Laravel タスクスケジュールまとめ
- サーバで
cron
エントリでタスクの定期実行することに近い - Laravel のスケジューラを使用すると、サーバには cron エントリがたった一つだけで済む
- タスクスケジュールは
app/Console/Kernel.php
のschedule()
に定義する
スケジューラを使う
以下が可能
- クロージャの呼び出し
- Artisan コマンドをスケジュールする
- Linux コマンドをスケジュールする
前提
- スケジューラを使用するには、サーバに下記のような
cron
エントリを追加する - 簡単に cron エントリを管理できる Laravel Forge のようなサービスをも存在する
# エディタが起動する
crontab -e
# Laravel コマンドスケジューラを毎分呼び出す
# スケジュールされているジョブを評価し、実行する必要のあるジョブを起動する
# 追記する。* * * * * にすると毎分実行の意味
# 最終行は改行しないと動かないので注意
* * * * * cd [プロジェクトのパス] && php artisan schedule:run >> /dev/null 2>&1
クロージャ定義のスケジュール
- この例は毎日深夜 12 時にクロージャをスケジュールする
- クロージャの中でテーブルをクリアするデータベースクエリを実行する
<?php
namespace App\Console;
use 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();
}
}
- クロージャを使用したスケジュールでは、invoke 可能なオブジェクトも使用可能
-
__invoke
メソッドを含む PHP クラスのこと
-
$schedule->call(new DeleteRecentUsers)->daily();
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();
シェルコマンドのスケジュール
exec()
$schedule->exec('node /home/forge/script.js')->daily();
ルール
繰り返しのスケジュールオプション
メソッド | 説明 |
---|---|
->cron('* * * * *'); |
Cron指定に基づきタスク実行 |
->everyMinute(); |
毎分タスク実行 |
->everyFiveMinutes(); |
5分毎にタスク実行 |
->everyTenMinutes(); |
10分毎にタスク実行 |
->everyFifteenMinutes(); |
15分毎にタスク実行 |
->everyThirtyMinutes(); |
30分毎にタスク実行 |
->hourly(); |
毎時タスク実行 |
->hourlyAt(17); |
一時間ごと、毎時17分にタスク実行 |
->daily(); |
毎日深夜12時に実行 |
->dailyAt('13:00'); |
毎日13:00に実行 |
->twiceDaily(1, 13); |
毎日1:00と13:00時に実行 |
->weekly(); |
毎週実行 |
->weeklyOn(1, '8:00'); |
毎週月曜日の8:00時に実行 |
->monthly(); |
毎月実行 |
->monthlyOn(4, '15:00'); |
毎月4日の15:00に実行 |
->quarterly(); |
四半期ごとに実行 |
->yearly(); |
毎年実行 |
->timezone('America/New_York'); |
タイムゾーン設定 |
// 週に一回、月曜の13:00に実行
$schedule->call(function () {
//
})->weekly()->mondays()->at('13:00');
// ウィークエンドの8時から17時まで一時間ごとに実行
$schedule->command('foo')
->weekdays()
->hourly()
->timezone('America/Chicago')
->between('8:00', '17:00');
追加のスケジュール制約のリスト
メソッド | 説明 |
---|---|
->weekdays(); |
ウイークデーのみに限定 |
->sundays(); |
日曜だけに限定 |
->mondays(); |
月曜だけに限定 |
->tuesdays(); |
火曜だけに限定 |
->wednesdays(); |
水曜だけに限定 |
->thursdays(); |
木曜だけに限定 |
->fridays(); |
金曜だけに限定 |
->saturdays(); |
土曜だけに限定 |
->between($start, $end); |
開始と終了時間間にタスク実行を制限 |
->when(Closure); |
クロージャの戻り値がtrueの時のみに限定 |
時間制限
-
between()
- 一日の時間に基づき、実行時間を制限する
$schedule->command('reminders:send')
->hourly()
->between('7:00', '22:00');
-
unlessBetween()
- その時間にタスクの実行を除外する
$schedule->command('reminders:send')
->hourly()
->unlessBetween('23:00', '4:00');
真値テスト制約
-
when()
- 指定した真値テストの結果に基づき制限を実行
- 指定したクロージャが true を返し、他の制約が実行を停止しない限りタスクを実行する
-
when()
をいくつかチェーンした場合、全部の when 条件が true を返すときのみスケジュールされたコマンドが実行する
$schedule->command('emails:send')->daily()->when(function () {
return true;
});
-
skip()
-
when()
をひっくり返したもの -
skip()
へ渡したクロージャが true を返した時、スケジュールタスクは実行される
-
$schedule->command('emails:send')->daily()->skip(function () {
return true;
});
タイムゾーン
-
timezone()
- タスクのスケジュールをどこのタイムゾーンとみなすか指定できる
$schedule->command('report:generate')
->timezone('America/New_York')
->at('02:00')
夏時間
- タイムゾーンの中には夏時間を取り入れているものがある
- 夏時間の切り替えにより、スケジュールされたタスクが2回実行されたり、全くされないことがある
- 可能であればタイムゾーンによるスケジュールは使用しないことが推奨される
タスク多重起動の防止
- デフォルトでは以前の同じジョブが起動中であっても、スケジュールされたジョブは実行される
- これを防ぐには
withoutOverlapping()
を使用する - 指定したタスクの実行時間の変動が非常に大きく、予想がつかない場合に特に便利
// emails:send という Artisanコマンドは実行中でない限り毎分実行される
$schedule->command('emails:send')->withoutOverlapping();
without overlapping を期限切れにする
- 時間切れまでのデフォルトは 24 時間
$schedule->command('emails:send')->withoutOverlapping(10);
単一サーバ上でのタスク実行
- アプリケーションが複数のサーバで実行されている場合、スケジュール済みのジョブを単一サーバ上でのみに制限して実行できる
前提
- この機能を使用するには、アプリケーションのデフォルトキャッシュドライバとして memcached か redis キャッシュドライバを使用する
- 全サーバが同じ単一のキャッシュサーバと通信している必要がある
ユースケース
たとえば、毎週の金曜の夜に、新しいレポートを生成するタスクをスケジュールしていると仮定する
タスクスケジューラが 3 つのワーカーサーバ上で実行されているなら、スケジュールされているタスクは 3 つ全部のサーバで実行され、3 回レポートが生成される
これを 1 回に制限したいようなケースに使う
実行
-
onOneServer()
- このタスクを最初に取得したサーバが、同じタスクを同じ cron サイクルで他のサーバが実行しないように、ジョブにアトミックなロックをする
$schedule->command('report:generate')
->fridays()
->at('17:00')
->onOneServer();
メンテナンスモード
- 通常のスケジュールタスクは、Laravel がメンテナンスモードの間は実行されない
- メンテナンスモードでも実行するように強制したい場合は
evenInMaintenanceMode()
を使用する
$schedule->command('emails:send')->evenInMaintenanceMode();
タスク出力
以下のメソッドは command()
と exec()
に対して排他的なので注意する
-
sendOutputTo()
- 後ほどタスク内容を調べられるようにファイルへ出力できる
$schedule->command('emails:send')
->daily()
->sendOutputTo($filePath);
-
appendOutputTo()
- タスク出力を指定したファイルに追記する
$schedule->command('emails:send')
->daily()
->appendOutputTo($filePath);
-
emailOutputTo()
- 出力をメールで送ることができる
- メール送信サービスの設定を済ませておく必要がある
$schedule->command('foo')
->daily()
->sendOutputTo($filePath)
->emailOutputTo('foo@example.com');
タスクフック
before()
-
after()
- スケジュールされたタスクの実行前後に指定したコードを実行できる
$schedule->command('emails:send')
->daily()
->before(function () {
// タスク開始時…
})
->after(function () {
// タスク終了時…
});
URLへの Ping
前提
- Guzzle HTTP ライブラリーが必要
composer require guzzlehttp/guzzle
実行
pingBefore($url)
-
thenPing($url$)
- タスク実行前、タスク完了後に指定したURLへ自動的に Ping することができる
- Laravel Envoyer のような外部サービスにスケジュールされたタスクが始まる、または完了したことを知らせるのに便利
$schedule->command('emails:send')
->daily()
->pingBefore($url)
->thenPing($url);