Laravelのタスクスケジュール機能を使った定期実行のやり方

はじめに

はじめまして、DXビジネス開発部フィールドソリューションユニットの田村です。
テックブログ初投稿です、温かい目で読んでもらえますと幸いです。

普段の業務ではバックエンドの開発を行っており、主にGolangやphp(Laravel)などを使用しています。 今回はLaravelのタスクスケジュール機能を利用する機会がありましたのでその方法について記載したいと思います。

目次

Laravelのタスクスケジュールとは

laravel.com

Laraveに標準で搭載されている機能でLaravelアプリケーションの中で定期実行したい処理を登録できます。つまりLaravelアプリケーションを立ち上げたサーバーを一つ用意し設定すればば、従来のようにサーバー内で複数cron書式の設定をする必要がなく、cron用のサーバーを別で管理する必要もなくなります。

基本的な使い方

スケジュール機能の基本的な使い方について紹介します。
まず実行したい処理は基本的にapp/Console/Kernel.phpのscheduleメソッドの中に書きます。
以下は1時間おきに「こんにちは」という文字列を出力するという内容です。
気分がいいですね。

class Kernel extends ConsoleKernel
{
    /**
     * Define the application's command schedule.
     *
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
      $schedule->call(function (){
        Log::info('こんにちは')
      })->hourly();
    }

〜〜以下略〜〜

時間の指定は割と柔軟に指定できるようなので公式ドキュメントをご確認いただけますと幸いです。

artisan commandの作成・登録

scheduleメソッドの中に書く処理はartisan commandとして登録したものを実行するのが一般的なようです。

以下でartisan commandを登録する雛形を作成できます。

php artisan make:command Greeting

app/Console/Commandsにファイルが作成されます。 実行する処理についてはhandleメソッドに書きます。 またcommandの名前や説明を追加できます。

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;

class Greeting extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'app:greeting';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '';

    /**
     * Execute the console command.
     */
    public function handle()
    {
         Log::info('こんにちは')
    }
}

コマンドを作成した後に、 作成したコマンドが登録されているか確認します

php artisan

artisanコマンドの一覧が表示されると思います。 さきほど登録したapp:greetingがあれば問題ありません。

scheduleの実行

Command登録後はscheduleメソッド内で先ほど登録したCommandを利用するようにします。

protected function schedule(Schedule $schedule)
    {
        $schedule->command('app:greeting')
          ->hourly();
    }

ここでスケジュールが正確に行われるか確認するコマンドを紹介します。
以下で処理が実行される時間やコマンド名などを確認できます

php artisan schedule:list

期待する処理がスケジュールされているようであれば処理を実行します。
サーバー側で1分おきにphp artisan schedule:runコマンドを叩くようにcronエントリを設定する必要があります。
複数のcron処理をLaravel側で登録し、実行時はcronの設定を一つ書くだけになるので非常に便利ですね。

以下公式ドキュメントから抜粋

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

またローカル環境で実行したい場合は以下のコマンドが利用できます。

php artisan schedule:work

単一サーバー上での実行について

続いて単一サーバー上での実行方法について紹介します。 ここまでのやり方で実際に運用をしていくとなると一つ問題があります。
一つのサーバーだけを起動して運用する場合であればこれまでの方法で問題ありませんが、複数のサーバーを起動するとなるとそうはいきません。
例えば負荷分散等で今回作ったLaravelアプリケーションのサーバーを3つ起動させる場合、それぞれのサーバーでスケジュールしていた処理が実行されてしまい、計3回の処理が走ってしまいます。それはさすがにまずいので1つのサーバーだけスケジュールした処理を実行するようにしたいですね。

onOneServerメソッドについて

そこで登場するのがonOneServerメソッドです。
名前の通り1つのサーバーでのみ実行するように制御できるメソッドです。
使い方としては以下のように追加するだけです。

protected function schedule(Schedule $schedule)
    {
        $schedule->command('app:greeting')
            ->hourly()
            ->onOneServer();     
    }

キャッシュドライバの設定について

この機能を利用するにあたってLaravel側でキャッシュドライバの設定が必要になります。 DB, Redis, Memcacheなどいろいろ利用できますが、今回はDBをキャッシュドライバとして使っていきます。

laravel.com

以下コマンドでキャッシュ用のテーブルを作成するマイグレーションファイルを作成します。

php artisan cache:table

テーブル作成後はLaravelアプリケーション側でキャッシュドライバの設定を変更します。 config/cache.phpにあるcacheのdefault設定を今回はdatabaseにします。

'default' => env('CACHE_DRIVER', 'database'),

以上で設定完了です。
実際に複数サーバーの環境で実行してみるとキャッシュドライバとして作成したcacheテーブルにレコードが追加されていると思います。

おそらく一番最初に実行しようとしたサーバーがDBにレコードを追加し、それ以降のサーバーはDBにレコードがあるため処理を行わないという流れになっていると思われます。 実際にサーバーのログを確認すると一つのサーバーだけ処理が走り、それ以外のサーバーではスキップされていることが確認できるかと思います。

おわりに

今回はLaravelのタスクスケジュールと単一サーバー上で実行させる方法について紹介しました。 注意事項として処理が大きすぎる場合はサーバーのリソースが不足する可能性がありますのでご注意ください。
とても手軽に実装できるのでLaravelをお使いの方は是非利用してみてください!

OPTiMではエンジニアを募集しております。
Laravelに限らず様々な言語やフレームワークを使ってプロダクト開発をしています、ご興味のある方は是非こちらをご覧ください。

  www.optim.co.jp