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に頼る