Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
97
Help us understand the problem. What is going on with this article?

More than 1 year has passed since last update.

@Takuma_Ikeda

Laravel タスクスケジュールまとめ

Laravel タスクスケジュールまとめ

  • サーバで cron エントリでタスクの定期実行することに近い
  • Laravel のスケジューラを使用すると、サーバには cron エントリがたった一つだけで済む
  • タスクスケジュールは app/Console/Kernel.phpschedule() に定義する

スケジューラを使う

以下が可能

  • クロージャの呼び出し
  • 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);

単一サーバ上でのタスク実行

  • アプリケーションが複数のサーバで実行されている場合、スケジュール済みのジョブを単一サーバ上でのみに制限して実行できる
前提
  • この機能を使用するには、アプリケーションのデフォルトキャッシュドライバとして memcachedredis キャッシュドライバを使用する
  • 全サーバが同じ単一のキャッシュサーバと通信している必要がある
ユースケース

たとえば、毎週の金曜の夜に、新しいレポートを生成するタスクをスケジュールしていると仮定する

タスクスケジューラが 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);

参考

タスクスケジュール 5.6 Laravel

97
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
97
Help us understand the problem. What is going on with this article?