search
LoginSignup
25

More than 3 years have passed since last update.

posted at

updated at

Laravelでスケジュールの並列実行

Laravelのタスクスケジュール機能を使用して並列処理を行う必要があったのでやり方を調べてみました。

スケジューラーという事なので当然時刻が被った場合も問題なく並列処理を行ってくれると思っていたのですがうまくいきませんでした。
並列処理のオプション等を見逃している可能性がありますのでありましたらご指摘ください。

実行コマンド(本来はcronから実行しますが、確認では直接実行して試しています)

php artisan schedule:run

クーロンに設定するならこんな感じ(環境によって参照するDBを変えている等の場合のために環境設定を付けておく)

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

クロージャーで設定してみる

app/Console/Kernel.php

use Illuminate\Support\Carbon;


    protected function schedule(Schedule $schedule)
    {
        $time = Carbon::now()->format('H:i');

        $schedule->call(function () {
            sleep(5);
            var_dump(1, Carbon::now());
        })->dailyAt($time);

        $schedule->call(function () {
            sleep(5);
            var_dump(2, Carbon::now());
        })->dailyAt($time);

        $schedule->call(function () {
            sleep(5);
            var_dump(3, Carbon::now());
        })->dailyAt($time);

        var_dump('test');

        // $schedule->command('inspire')
        //          ->hourly();
    }

Schedule内部ではCarbonを使用しているのでCarbonで合わせました。

実行結果

# php artisan schedule:run
string(4) "test"
Running scheduled command: Closure
int(1)
object(Illuminate\Support\Carbon)#3241 (3) {
  ["date"]=>
  string(26) "2018-02-27 22:55:55.862911"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(10) "Asia/Tokyo"
}
Running scheduled command: Closure
int(2)
object(Illuminate\Support\Carbon)#3242 (3) {
  ["date"]=>
  string(26) "2018-02-27 22:56:00.864827"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(10) "Asia/Tokyo"
}
Running scheduled command: Closure
int(3)
object(Illuminate\Support\Carbon)#3243 (3) {
  ["date"]=>
  string(26) "2018-02-27 22:56:05.867566"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(10) "Asia/Tokyo"
}

結果

同期処理

※上記を見ると指定時刻が過ぎても実行はされる様子。

$time = Carbon::now()->addMinute(1)->format('H:i');

としたところ1つも実行されなかった。

先にdailyAt設定時に
vendor/mtdowling/cron-expression/src/Cron/CronExpression.php
isDueで判定される様子。
クロージャーは後から実行されました。

Artisanコマンドにしてcommandで設定してみる

php artisan make:command ParallelSchedule
app/Console/Commands/ParallelSchedule.php

use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;

class ParallelSchedule extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:batch {id?}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Parallel processing for schedule';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $id = $this->argument('id');
        Log::info((string)$id.':'.(string)Carbon::now());
        sleep(5);
    }
}
app/Console/Kernel.php
protected $commands = [
    Commands\ParallelSchedule::class
];

protected function schedule(Schedule $schedule)
{
    $time = Carbon::now()->format('H:i');

    $schedule->command('command:batch 1')->dailyAt($time);
    $schedule->command('command:batch 2')->dailyAt($time);
    $schedule->command('command:batch 3')->dailyAt($time);

    // $schedule->command('inspire')
    //          ->hourly();
}

実行結果

# php artisan schedule:run
Running scheduled command: '/usr/bin/php' 'artisan' command:batch 1 > '/dev/null' 2>&1
Running scheduled command: '/usr/bin/php' 'artisan' command:batch 2 > '/dev/null' 2>&1
Running scheduled command: '/usr/bin/php' 'artisan' command:batch 3 > '/dev/null' 2>&1
[2018-02-27 23:39:04] test.INFO: 1:2018-02-27 23:39:04  
[2018-02-27 23:39:11] test.INFO: 2:2018-02-27 23:39:11  
[2018-02-27 23:39:17] test.INFO: 3:2018-02-27 23:39:17  

結果

同期処理

Artisanコマンドにしてexecを設定してみる

app/Console/Kernel.php
    protected function schedule(Schedule $schedule)
    {
        $time = Carbon::now()->format('H:i');

        $schedule->exec('php '.base_path().'/artisan command:batch 1 --env=production')->dailyAt($time);
        $schedule->exec('php '.base_path().'/artisan command:batch 2 --env=production')->dailyAt($time);
        $schedule->exec('php '.base_path().'/artisan command:batch 3 --env=production')->dailyAt($time);

        // $schedule->command('inspire')
        //          ->hourly();
    }

実行結果

# php artisan schedule:run
Running scheduled command: php /home/public/app/artisan command:batch 1 --env=production > '/dev/null' 2>&1
Running scheduled command: php /home/public/app/artisan command:batch 2 --env=production > '/dev/null' 2>&1
Running scheduled command: php /home/public/app/artisan command:batch 3 --env=production > '/dev/null' 2>&1
[2018-02-27 23:48:57] test.INFO: 1:2018-02-27 23:48:57  
[2018-02-27 23:49:03] test.INFO: 2:2018-02-27 23:49:03  
[2018-02-27 23:49:10] test.INFO: 3:2018-02-27 23:49:10  

結果

同期処理

PHP標準のexec()をクロージャー内で使ってみる

app/Console/Kernel.php
    protected function schedule(Schedule $schedule)
    {
        $time = Carbon::now()->format('H:i');

        $schedule->call(function () {
            //  > /dev/null & でレスポンスを待たない
            exec('php '.base_path().'/artisan command:batch 1 --env=production > /dev/null &');
            exec('php '.base_path().'/artisan command:batch 2 --env=production > /dev/null &');
            exec('php '.base_path().'/artisan command:batch 3 --env=production > /dev/null &');
        })->dailyAt($time);

        // $schedule->command('inspire')
        //          ->hourly();
    }

実行結果

# php artisan schedule:run
Running scheduled command: Closure
[2018-02-28 00:05:43] test.INFO: 2:2018-02-28 00:05:43  
[2018-02-28 00:05:43] test.INFO: 1:2018-02-28 00:05:43  
[2018-02-28 00:05:43] test.INFO: 3:2018-02-28 00:05:43

結果

非同期処理

runInBackgroundを使用してみる

(コメントで教えていただきました。)

クロージャーで設定してみる(runInBackground)

app/Console/Kernel.php
    protected function schedule(Schedule $schedule)
    {
        $time = Carbon::now()->format('H:i');

        $schedule->call(function () {
            sleep(5);
            var_dump(1, Carbon::now());
        })->dailyAt($time)->runInBackground();

        $schedule->call(function () {
            sleep(5);
            var_dump(2, Carbon::now());
        })->dailyAt($time)->runInBackground();

        $schedule->call(function () {
            sleep(5);
            var_dump(3, Carbon::now());
        })->dailyAt($time)->runInBackground();
    }

実行結果

# php artisan schedule:run
Running scheduled command: Closure
int(1)
object(Illuminate\Support\Carbon)#4905 (3) {
  ["date"]=>
  string(26) "2018-02-28 23:59:11.466811"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(10) "Asia/Tokyo"
}
Running scheduled command: Closure
int(2)
object(Illuminate\Support\Carbon)#4906 (3) {
  ["date"]=>
  string(26) "2018-02-28 23:59:16.467282"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(10) "Asia/Tokyo"
}
Running scheduled command: Closure
int(3)
object(Illuminate\Support\Carbon)#4907 (3) {
  ["date"]=>
  string(26) "2018-02-28 23:59:21.467938"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(10) "Asia/Tokyo"
}

結果

同期処理

Artisanコマンドにしてcommandで設定してみる(runInBackground)

app/Console/Kernel.php
    protected function schedule(Schedule $schedule)
    {
        $time = Carbon::now()->format('H:i');

        $schedule->command('command:batch 1 --env=production')->dailyAt($time)->runInBackground();
        $schedule->command('command:batch 2 --env=production')->dailyAt($time)->runInBackground();
        $schedule->command('command:batch 3 --env=production')->dailyAt($time)->runInBackground();
    }

実行結果

# php artisan schedule:run
Running scheduled command: ('/usr/bin/php' 'artisan' command:batch 1 --env=production > '/dev/null' 2>&1 ; '/usr/bin/php' 'artisan' schedule:finish "framework/schedule-d72fef692ea6d086899b393775691de2c59d998b") > '/dev/null' 2>&1 &
Running scheduled command: ('/usr/bin/php' 'artisan' command:batch 2 --env=production > '/dev/null' 2>&1 ; '/usr/bin/php' 'artisan' schedule:finish "framework/schedule-fece6325cae11a5e0d0771be0ffe2207c4822238") > '/dev/null' 2>&1 &
Running scheduled command: ('/usr/bin/php' 'artisan' command:batch 3 --env=production > '/dev/null' 2>&1 ; '/usr/bin/php' 'artisan' schedule:finish "framework/schedule-e3321e90e5e5f89efc46e6cecd0c8b2441bac03a") > '/dev/null' 2>&1 &
[2018-03-01 00:05:39] test.INFO: 3:2018-03-01 00:05:39  
[2018-03-01 00:05:39] test.INFO: 2:2018-03-01 00:05:39  
[2018-03-01 00:05:39] test.INFO: 1:2018-03-01 00:05:39  

結果

非同期処理

Artisanコマンドにしてexecで設定してみる(runInBackground)

app/Console/Kernel.php
    protected function schedule(Schedule $schedule)
    {
        $time = Carbon::now()->format('H:i');

        $schedule->exec('php '.base_path().'/artisan command:batch 1 --env=production')->dailyAt($time)->runInBackground();
        $schedule->exec('php '.base_path().'/artisan command:batch 2 --env=production')->dailyAt($time)->runInBackground();
        $schedule->exec('php '.base_path().'/artisan command:batch 3 --env=production')->dailyAt($time)->runInBackground();
    }

実行結果

# php artisan schedule:run
Running scheduled command: (php /home/public/app/artisan command:batch 1 --env=production > '/dev/null' 2>&1 ; '/usr/bin/php' 'artisan' schedule:finish "framework/schedule-ff228d98e15354119ff37ba8d14716d0ecc420fa") > '/dev/null' 2>&1 &
Running scheduled command: (php /home/public/app/artisan command:batch 2 --env=production > '/dev/null' 2>&1 ; '/usr/bin/php' 'artisan' schedule:finish "framework/schedule-9360705c67bd8666b81b8f8948dd63f611012551") > '/dev/null' 2>&1 &
Running scheduled command: (php /home/public/app/artisan command:batch 3 --env=production > '/dev/null' 2>&1 ; '/usr/bin/php' 'artisan' schedule:finish "framework/schedule-1faae3f394252d1eb297830de3be729f104fb086") > '/dev/null' 2>&1 &
[2018-03-01 00:08:14] test.INFO: 2:2018-03-01 00:08:14  
[2018-03-01 00:08:14] test.INFO: 1:2018-03-01 00:08:14  
[2018-03-01 00:08:14] test.INFO: 3:2018-03-01 00:08:14  

結果

非同期処理

まとめ

  • 並列処理を行いたい場合はArtisanコマンド化が必要
  • runInBackgroundを設定する(commandとexecは効くが、クロージャーには効かない)
  • runInBackgroundを使わない場合はPHP標準のexecに頼る

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
25