📃

WindowsでLaravelのスケジュールタスク設定(後編)

2021/08/09に公開

前回はwindowsでのスケジュールタスクの設定についてまとめました。後編ではスケジュールタスクの使い方について少し触れます。

スケジュールタスクを書く

公式では詳しい例が多いので、ここはよく使うスケジュールジョブのパターンを挙げます。

タスクはもちろん、同期処理のタスクでも良いし、非同期のものでも構いません。データ量が多い場合は基本的に非同期一択です。Laravelではジョブとの概念を抽象化して、非同期処理をジョブをディスパッチする形にまとめています。
まずはジョブをコマンドで作ります:

php artisan make:job ProcessSync

これでappのフォルダーにJobsのフォルダーが作られ、ジョブクラスのテンプレートが書き込まれます。その中、handleメソッドがジョブの実行時にコールされる関数となりますので、メインの処理は全部handleに書き込みます。ジョブにデータを引き渡したい場合、コンストラクターに定義できます。

class ProcessSync implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    private $log_msg;
    private $system;
    private $user_id;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct(String $system, int $user_id = 0)
    {
        $this->system = $system;
        $this->user_id = $user_id;
        $this->log_msg = '';
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        $log_id = $this->startLog($this->system);
        $result = $this->syncWithSystem($this->system);
        $this->updateLog($log_id, $result, $this->log_msg); 
    }

    private function syncWithSystem(String $system)
    {...}
    
    private function startLog(String $system)
    {...}
    
    private function updateLog(int $log_id, bool $result, String $log_msg)
    {...}
    
    private getResult() 
    {...}

}

それでジョブをスケジュールに加えます。例えば、毎日の3回、8時間置きに実行したい時に、cronメソッドを使います。他にもeveryMinute、every...、daily、dailyAt、などいっぱいありますので公式ドキュメントにご参照ください。細かく決めたいときは基本的にcronに頼ります。

class Kernel extends ConsoleKernel
{
    protected $commands = [
        //
    ];

    protected function schedule(Schedule $schedule)
    {
        $schedule->job(new ProcessSync('1'))->cron('0 0,8,16 * * *');
    }

    protected function commands()
    {
        $this->load(__DIR__ . '/Commands');

        require base_path('routes/console.php');
    }

    // 念のためタイムゾーンも
    protected function scheduleTimezone()
    {
        return 'Asia/Tokyo';
    }
}

補足ですが、ジョブはもちろんコントローラからもディスパッチは可能です。時には、実行の結果を引数として使いたい、という場合は、同期処理にすることで可能となります。

// とあるコントローラで
$job = new ProcessSync($system, $request->user->id);
$this->dispatch($job);
// 同期に処理したい場合
$this->dispatchNow($job);
$result = $job->getResult();

ジョブテーブルについて

ジョブについて一度変な「バグ」と会いました。databaseをデフォルトのジョブキューとして、ジョブのテーブルを作りました。しかし、テストするときに、ジョブテーブルに何のレコードも挿入されません。

ちょっと変だなと思って色々と調べましたが、どうやら、

  • jobsというテーブルは、QUEUE_DRIVER = database (設定済み)の場合のみ使用されます

  • dispatchが実行されるときに、jobsにレコードが挿入されます

  • ジョブが完了または失敗しましたら、レコードが削除されます(正直は残して欲しいですね、せめて残すかどうかを選択できるように)

  • そのため、今現在タスクキューにジョブが残っている(並んでいる)限り、jobsテーブルにレコードが見えます

検証するために、ワーカーを止めて何回かジョブをディスパッチすると、キューに溜まっていき、jobsテーブルにも見えてきます。それでphp artisan queue:workでワーカーを立ち上げると、ジョブは消化されてしまい、またjobsが空に戻ります。

以上です。

Discussion