xin9le.net

Microsoft の製品/技術が大好きな Microsoft MVP な管理人の技術ブログです。

IIS上のPHP環境で気を付けるべき3つのタイムアウト制御

C#/.NET はサーバーからクライアントまで、そして今となっては iOS / Android などのモバイルでも使える超オールマイティな言語です。たったひとつの言語で一気通貫した開発ができることは、極めて効率的と言えます。

一方 PHP は Web 向けの言語です。それ以外の用途には基本的に向きません。LAMP 環境を至高と考えている人の中には「銀の弾丸」かのような勘違いをしている人もいるかもしれませんが、あくまでも Web 用。適材適所です。

最近 PHP で困ったこと

そんな中、最近業務で前任者が実装した PHP でバッチ処理 をしているコードと対峙することになりました。Web API として実装されており、POST アクセスの先で都度データベースに何万回も insert 文を投げるというものです。Linux + Apache 上で動いていたそれを新たに Windows Server 上の IIS にホストすることとなり、PHP ファイルを移しました。いざ実行してみると、途中まで何事もなく成功しているようなのに突然 catch 句にも入らずエラーになる。しかもエラー時にデータベースがロールバックされずにコミットされててテンヤワンヤ。心身ともにモゲそうになりつつ調べていくと、どうやら 30 秒くらいでタイムアウトしているらしい事が分かりました。

「PHP で Web API ベースのバッチなんて書くから...(云々」という至極当然な気持ちをグッと堪え、とりあえずタイムアウト時間を延ばして回避したので、今回はその方法についてのメモ。

PHP の最大実行時間を延ばす

PHP のタイムアウトでまず最初に確認すべきなのが php.ini に書かれた最大実行可能時間の設定です。これを延ばすことでスクリプトがパーサにより強制終了されるのを防ぐことができます。既定値は 30 秒になっています。

; Maximum execution time of each script, in seconds
; http://php.net/max-execution-time
; Note: This directive is hardcoded to 0 for the CLI SAPI
max_execution_time = 30

PHP Manual には以下のように書かれており、「そりゃそーだ」と思うと共に、完全に白目になりますね...。

スクリプトがパーサにより強制終了されるまでに許容される最大の 時間を秒単位で指定します。この命令は、いい加減に書かれたスクリプトがサーバーの負荷を上げることを防止するのに役立ちます。

Fast CGI の要求タイムアウトを延ばす

IIS の場合、先の PHP の最大実行時間を延ばしてもまだダメな場合があります。IIS 自体が、PHP の実行のために呼び出した Fast CGI をタイムアウトさせる場合があるためです。この設定を延ばします。

Fast CGIの設定

上記の画面から [要求タイムアウト] の設定を延ばせば OK です。また、一緒に [アクティビティ タイムアウト] の時間も延ばしておきましょう。サーバー側の設定はこれで完了です。

クライアントからの呼び出し時間を延ばす

最後にもう一点。クライアント側がサーバーに処理を要求したものの、その完了を待ち切れずに処理を中止してしまっては意味がありません。クライアント側のタイムアウト時間も必要に応じて延ばしましょう。まぁ PHP 関係なく、当然ですね。

var client = new HttpClient();
client.Timeout = TimeSpan.FromMinutes(10);  //--- TimeSpan指定
var response = await client.PostAsync(...);
var request = WebRequest.Create("...");
request.Timeout = 10 * 60 * 1000;  //--- ミリ秒指定
request.Method = "POST";
var response = await request.GetResponseAsync();

呼び出しに利用するライブラリによって指定の仕方が違うので、そこだけは気を付けたいですね。

まとめ

クライアント/サーバー型のアプリケーションの場合、タイムアウトが発生したときはどこか一か所だけ直せば済むという事は少ないかもしれません。タイムアウト設定がある箇所をひとつひとつ確認するのが大事と思います。そしてできる限り、タイムアウトが発生するような仕組みの上で重たいバッチ処理をするのは止めましょう