エックサーバーはcronが利用できます。cronはWindowsでいうatコマンドやスケジューラーみたいな機能です。
お好みのプログラムを10分毎に処理を実行させたり、毎日決まった時間に実行させることができます。
cronが使えるレンタルサーバーは、さくらレンタルサーバー、ロリポップ!などcron レンタルサーバーで探すことで対応しているレンタルサーバーが見つかります。
登録できるスケジュールの数、動いたスケジュールの稼働時間など何かしらのリミットがもうけられています。エックサーバーの場合は、CPUを100%使って良い時間に制限があります。
1回の実行で許容されるCPUフルの時間が30秒を超えると、そのプログラムが強制終了されます。
具体的には、Maximum execution time of 30 seconds exceededというメッセージとともに強制終了されます。
sleepやusleepで実行率を下げればいける!?と思いましたが、完了するまでの時間を伸ばすだけで、実処理のCPU時間の上限は変わりません。考えると当たり前ですね・・そのためほぼ同じ位置で処理が止まります。
CPUをぶん回し、30秒以内に完了するプログラムは、そのままなんの工夫もしないでいけます。
30秒をちょっとでも超えるプログラムは、強制的にプロセスが終了してしまうので対策が必要です。
エックスサーバーで30秒以上のCronを上手に運用する3つのコツ
これからご紹介するのは、エックスサーバーに限らず、PHPをcron実行させ、時間制限のあるサーバーでCronを使うためのコツとして使えます。
同じ処理を何度もcron実行させ、目的の処理を完了させるように変更します。
コツ1 ループ処理の実行時間を計測し、強制終了する前に中断する
microtime()で経過時間を測定します。これは、強制終了で中途半端な位置で終了を防ぐ目的です。
以下のようなループ回数が多いパターンなどで使えます。
foreach( $datas as $key => $data ){
1つあたりの処理時間は大したことがない関数();
ループ回数が多いと強制終了される
}
このように実行から28秒経過した時点でループ処理を抜けることが可能になります。
$st = microtime(true);
$実行時間タイムアウト=false;
foreach( $datas as $key => $data ){
if( (microtime(true) - $st ) >= 28 ){
$実行時間タイムアウト=true;break;
}
1つあたりの処理時間は大したことがない関数();
ループ回数が多いと強制終了される
}
if( $実行時間タイムアウト ){
// タイムアウト用処理
}
完了処理();
- microtime(true) 現在の Unix タイムスタンプをマイクロ秒単位で取得します。
- 実行前に記録した開始時間stと引き算することでおおよその処理時間を求めています。
この例では28秒経過で処理を中断しています。 - より精度を高めたい場合は、microtimeの代わりにhrtimeを利用します。
これで許容されるCPUフルの時間前に処理を停止することが可能になります。
コツ2 処理結果を記録する
コツ1で実行時間を判断することが可能になりました。
何も工夫しない状態では、毎回先頭から始まってしまうので、何度やっても同じところで止まってしまいます。
これを回避するにはやった処理を記録し、スキップさせるような仕組みを導入します。
$st = microtime(true);
$実行時間タイムアウト=false;
$進捗=array(); if( file_exists("進捗.json") ){ $進捗 =json_decode(file_get_contents("進捗.json"),true);}
foreach( $datas as $key => $data ){
if( array_key_exists( $key , $進捗 ) == true) { continue;}
if( ( microtime(true) - $st ) >= 28 ){
$実行時間タイムアウト=true;break;
}
1つあたりの処理時間は大したことがない関数();
$進捗[$key]=true;
ループ回数が多いと強制終了される
}
file_put_contents( "進捗.json", json_encode($進捗) );
if( $実行時間タイムアウト ){
// タイムアウト用処理
}
完了処理();
実行した処理を処理済み配列$進捗に格納していきます。ファイルに保存して、次回実行時に読み込まれるので、複数回呼ばれても同じ処理を実行することがなくなります。
スキップさせたい単位の処理済み配列は、この例では、キーワード単位にしています。粒度はご自身の処理に合わせて変更することが可能です。
- 進捗.jsonファイルのような中間ファイルを用意する場合、ファイルサイズに気をつけてください。大きなファイルになる場合、メモリ関係のエラー問題が起きる場合があります。
- そのような場合は、sqlite3を利用して管理することで大きなサイズでも扱いやすくなります。
コツ3 throwを使ってシンプルにする
コツ1、コツ2で大抵対応できます。throwを使うと処理をシンプルに書くことが可能です。
時間を計測して、タイムアウトを検出したら、次の処理に進まずに処理を終了させたいですよね。
この際、throwを使います。
function main(){
try{
前処理();
時間計測している処理1();
時間計測している処理2();
時間計測している処理3();
後処理();
}catch(Exception $e ){
}
}
function 時間計測している処理1(){
ループ処理でタイムアウトを検出、throw new Exception("タイムアウト処理1");
}
メイン処理の中から個別に呼び出した処理でそれぞれタイムアウトを検出するようにしています。
通常、処理1が終了すると続けて、2、3、と流れていきます。throw new Exception(“”)で例外を発生させると次の処理に進まずに、メイン処理のcatch部に流れてきます。
これにより、メイン処理の構造を変えずに、シンプルに処理を中断させることができます。
おまけ PHPのバージョン指定を忘れずに
エックスサーバーは複数のバージョンのPHPを利用することができる環境になっています。
そのため、通常何もバージョンを指定しないphpは、PHP 5.3.3 です。
PHPのバージョンの違いによって思わぬ副作用があったりするので、必ずバージョンを指定して利用しましょう
PHP7系は以下のパスにあります。
/usr/bin/php7.1 (PHP 7.1.2)
/usr/bin/php7.0 (PHP 7.0.16)
おまけ2 cronの設定はsshからできるよ
エックスサーバーのコンソール(ブラウザ)からスケジュールを指定できます。
これ以外に、sshログインした後、以下コマンドでスケジュールを編集することができます。
$ crontab -e
viの使い方や、cronの書き方がわからない場合は、ググると詳しいサイトが見つかります。
先頭に#をつけるとコメントアウトできます。もし何か異常があったら先頭に#をつけて停止しておきましょう。
以下コマンドでスケジュールの一覧を確認することができます。
$ crontab -l
まとめ
ここまで、エックスサーバーで30秒以上のCronを上手に運用する3つのコツをご紹介してきました。
この記事にたどり着いた方はコアな方に違いない!って思ってます。
コメント