PHPを多重起動させたくない場合
▼ 例えばこういうケース
- あるスクリプトをCronなどで定期実行
- その間は同一スクリプトの起動を禁止
- 複数プロセスを存在させたくない
Cronとかでバックグラウンド処理する場合など、
多重起動されると困ることもあります。
そこでPHPスクリプトの多重起動防止策を紹介。
いくつか方法があるのでまとめてみました。
※ 間違いはコメントでご指摘ください
このページの目次
方法1.lockファイルを作って多重起動を回避
一時的なlockファイルを作ります。
それを目印に起動してるかを確かめられます。
もし作成済みなら起動させないというやり方です。
▼ 例 : こういうコードを書いてみた
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
///*** ///* ロックファイルのパス ///* もしCronJob内なら絶対パスを使う ///*** $lock_file = './test.lock'; if ( ! file_exists($lock_file) ) { /// ロックファイルを一時作成 $fp = fopen($lock_file, 'w+'); /// 模擬的な処理... for($i = 0; $i < 100; $i++){ sleep(1); } /// ロックファイルを後始末 fclose($fp); unlink($lock_file); } else { /// 既起動中ならそのまま終了 echo 'Already runnning!!'; } |
ファイルの存在有無で多重起動を判定してるだけ
一番わかりやすいかなと思います。
この方法の欠点はCrash・エラーに対応できないこと
当然ながら以下の状況も考えられます
- コードが何かしらのエラーをはく
- クラッシュで強制終了する
- Ctrl+Cなどで手動終了される
こういったケースには対応できません。
なぜなら fopen() を使って作ったlockファイルは終了時に自動削除されません。そしてPHPにはlockファイルを必ず削除するような関数も存在しないです。
つまり unlink($lock_file); によって明示的な削除が必要ということ。クラッシュ・予期せぬ終了をしたら一時ファイルはそのまま残ります。
だから絶対にコードがクラッシュする心配がない・予期せぬシャットダウンが起きない状況だったなら、この方法が使えるかもです。(つまり確実性に欠ける)
方法2.flockによる排他的ロックで多重起動を防ぐ
この方法ではファイルロックを使います。
具体的には排他的ロック(LOCK_EX)
これを上手く使うことで多重起動を防げます。
▼ 例 : こういうコードを書いてみた
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/// ロックファイルを一時作成 $file = fopen("test.lock","w+"); /// 排他的ロックを試行 if (flock($file,LOCK_EX + LOCK_NB)) { /// 模擬的な処理... for($i = 0; $i < 100; $i++){ echo 'runnning...'.PHP_EOL; sleep(1); } /// ロック解除 flock($file,LOCK_UN); } else { echo "Already running!!"; } fclose($file); |
▼ 例えば手動で1つ目のプロセスを起動
1 2 3 4 |
php test.php running... running... ... |
▼ 起動中に多重起動させようとすると…
1 2 |
php test.php Already running!! |
このコードのflockのロックモードについて
- LOCK_EX
排他的なロック。このフラグ単体だけの場合、他プロセスがアクセスした時に解除されるまでブロックするという挙動になる
- LOCK_BN
ブロックの挙動に関するフラグ。これがセットされている場合、ブロックが発生しない(つまりロック解除を待たない)
そしてプロセスが予期せぬシャットダウン/クラッシュした場合でも対応が可能です。自動的にファイルロックが解除されるため
また LOCK_BN を付与しなかった場合、排他的ロックが解除されるまでコードが一時停止します。リソース的なこともあるので LOCK_BN は付けた方が無駄がないです
ちなみにPHPにおけるflockのOS的な互換性について
このflockはほぼ全プラットフォームで使えます
▼ 公式リファレンスの説明
flock() を使うと、(ほとんどの Unix、そして Windows さえ含む) 事実上すべてのプラットフォームで使用可能な、簡易な読み手/書き手モデルを実現できます。ロックの解放は、fclose() が実行されるか、 stream がガベージコレクションされた段階で行われます。
しっかりと「Windowsさえ含む」と書いてあります。
だからテスト環境に依らず汎用的に使えます。
多重起動を防止するには排他的ロックを使えばいい
むしろ他の方法とかあるんですかね?
プラットフォーム・OSに依存しない、そして確実に多重起動を防止させたいなら…今紹介したflockによる排他的ロックを利用するのが一番だと思います。
もしこれ以外の方法(ライブラリ・仕組み)を知っている方がいればコメント欄からお教えください。僕も新しい知識を知るの好きなので大歓迎です。
以上、PHPでの多重起動防止策でした。ではまた