「長い」PHPスクリプトの問題

作業に時間がかかるスクリプトを記述することが必要になる場合があります。 たとえば、バックアップの作成/展開、一部のアプリケーションのデモ版のインストール、大量のデータの集約、データのインポート/エクスポートなどのスクリプト。 そのようなスクリプトが予期しない瞬間に動作を停止しないようにするには、いくつかのことを知って覚えておく必要があります。



外部タイムアウト



まず、PHP構成のmax_execution_timeパラメーターに適切な値を設定する必要があります。



スクリプトがWebサーバーによって起動された場合(つまり、ユーザーからのHTTP要求に応答して)、Webサーバーの構成でタイムアウトパラメーターを正しく構成する必要があります。 Apacheの場合、これらはパラメータTimeOutおよびFastCgiServer ... -idle-timeout ... (PHPがFastCGIを介して動作する場合)、nginx send_timeoutおよびfastcgi_read_timeout (PHPがFastCGIを介して動作する場合)です。



Webサーバーは、PHPスクリプトを実行する別のWebサーバーにリクエストをプロキシすることもできます(珍しい例ではなく、nginxがフロントエンド、apacheがバックエンドです)。 この場合、プロキシWebサーバーもプロキシタイムアウトを構成する必要があります。 apache ProxyTimeoutの場合、nginx proxy_read_timeoutの場合



ユーザーの中断



HTTPリクエストへの応答としてスクリプトが起動された場合、ユーザーはブラウザでリクエストを停止できます。その場合、PHPスクリプトも動作を停止します。 リクエストが停止された後でもスクリプトが作業を続行する必要がある場合は、PHP構成のignore_user_abortパラメーターをTRUEに設定します。



開いている接続の損失



スクリプトがサービス/サービス(データベース、電子メールサーバー、FTPサーバーなど)との接続を開き、スクリプトの実行中に接続がしばらく使用されない場合、このサービスによって接続を閉じることができます。 たとえば、スクリプトの実行中にMySQLクエリをしばらく実行しないと、MySQLはwait_timeoutパラメーターで指定された時間後に接続を閉じます。 その結果、次のリクエストを実行しようとするとエラーが発生します。



そのような場合、最初に接続タイムアウトを増やしてみてください。 たとえば、MySQLの場合、クエリを実行できます( Snowlyに感謝)

SET SESSION wait_timeout = 9999
      
      





これが不可能な場合、またはこのオプションが何らかの理由で適切でない場合、ダウンタイムが可能なコードの場所で接続アクティビティを確認し、必要に応じて再接続できます。 たとえば、MySQLiモジュールには、接続アクティビティをチェックするための便利な関数mysqli :: pingと、接続が切断されたときに自動的に再接続するためのmysqli.reconnect構成パラメーターがあります。 他のタイプの接続に同様の機能がない場合は、自分で作成してみることができます。 その中で、サービスに簡単にアクセスし、エラーの場合(try ... catch ...を使用してキャッチ)、再接続する必要があります。 例えば

 class FtpConnection { private $ftp; public function connect() { $this->ftp = ftp_connect('ftp.server'); ... } public function reconnect() { try { if (!ftp_pwd($this->ftp)) $this->connect(); } catch($e) { $this->connect(); } } ... }
      
      





または

 class MssqlConnection { private $db; public function connect() { $this->db = mssql_connect('mssql.server'); ... } public function reconnect() { try { if (!mssql_query('SELECT 1 FROM dual', $this->db)) $this->connect(); } catch($e) { $this->connect(); } } ... }
      
      







並列起動



多くの場合、長いスクリプトは(cronによって)スケジュールに従って実行され、スクリプトのコピーは一度に1つしか動作しないことが予想されます。 しかし、前のスクリプトが作業を完了する前に次のスクリプトの起動が発生する可能性があり、これは原則として望ましくありません(同じデータが2回インポートされ、最初のスクリプトで使用されたデータが消去されます...)。



このような場合、使用されるリソースのブロックを使用できますが、このタスクは常に個別に解決されます。 または、このスクリプトの別のコピーが実行されているかどうかを単純に確認し、動作が完了するまで待つか、現在の実行を完了することができます。 これを行うには、実行中のプロセスのリストを表示するか、次のようなスクリプト起動ロックを使用できます。



 if (lockStart('script.php')) { //    ... lockStop('script.php'); }
      
      







Webサーバーの負荷



長いスクリプトがWebサーバーを介して実行される場合、この同じWebサーバーへのクライアントの接続は、スクリプトが実行されるまで開いたままになります。 これは良くない、なぜなら Webサーバーのタスクは、可能な限り迅速に要求を処理し、結果を返すことです。 接続がハングしたままの場合、Webサーバーワーカー(プロセス)の1つが長時間ビジー状態になります。 そして、同時に多くのそのようなスクリプトが起動された場合、それらはすべて(まあ、またはほぼすべて)の無料のワーカー(ApacheについてはMaxClientsを参照)を取ることができ、Webサーバーは他のリクエストを処理できません。



したがって、ユーザーのリクエストを処理するとき、ウェブサーバーをロードしないように、php-cliを介してバックグラウンドでスクリプトを実行します。ユーザーはリクエストが処理されていることに答える必要があります。 必要に応じて、AJAX要求を使用して処理のステータスを定期的に確認できます。



それは、おそらく、このトピックで私が伝えることができるすべてです。 それが誰かに役立つことを願っています。



All Articles