前提: ここで話しているのは、典型的なlnmp構造、nginx+php-fpmモードです
コード内に sleep() が含まれていても実行が非常に遅い php プログラムがある場合、ブラウザがサービスに接続すると php-fpm プロセスが開始されますが、この時点でブラウザが閉じていると、このとき、サーバー上の php-fpm プロセスは実行され続けますか?
今日はこの問題を解決します。
最も簡単な実験
最も簡単な方法は、プログラムを書いてみましょう: file_put_contents を使用してスリープの前後にログを書き込みます:
実際の操作の結果は、サーバーがスリープしている間にクライアントのブラウザを閉じると、ログに 2222 が書き込まれることになります。
これは、ブラウザを閉じた後も、サーバー側の PHP は引き続き実行されることを意味しますか?
ignore_user_abort
Lao Wang と diogin は、これが PHP のignore_user_abort 関数に関連している可能性があることを思い出させました。
そこで、コードを次のように少し変更しました:
リーリーソフトウェアの使用がないことがわかり、ignore_user_abort がどのような値に設定されても実行され続けます。
しかしここで質問があります: user_abort とは何ですか?
このドキュメントでは、PHP スクリプトが実行され、ユーザーがスクリプトを終了すると、中断がトリガーされることが明確に説明されています。次に、スクリプトは、ignore_user_abort に基づいて実行を継続するかどうかを決定します。
しかし、公式ドキュメントには CGI モードでの中止について明確に記載されていません。クライアントが切断されても、CGI モードの PHP はアボートを受け取らないような気がします。
ignore_user_abort は CGI モードでは効果がありませんか?
心拍の問題ですか?
最初に思い浮かぶのは心拍の問題でしょうか?ブラウザ クライアントを切断することは、クライアントを閉じずに接続を切断することと同じであり、サーバーは TCP キープアライブを検出する前に到着するのを待つ必要があります。
まず、ブラウザ設定でキープアライブの問題をトラブルシューティングする必要があります。
ブラウザを放棄してクライアント プログラムを作成します。プログラムは http サービスに接続した後、ヘッダーを送信し、接続をアクティブに閉じる前に 1 秒間スリープします。ただし、このプログラムには http キープアライブ ヘッダーがありません。
手順は以下の通りです
リーリーサーバープログラム:
ignore_user_abort が設定されているかどうかに関係なく、PHP は依然として同じであることがわかりました。どうやら、ignore_user_abort はまだ有効になっていないようです。
ignore_user_abort をトリガーする方法
ignore_user_abort をトリガーするにはどうすればよいですか?サーバーはこのソケットが使用できないことをどのようにして知るのでしょうか? Lao Wang と Diogin は、ソケットが使用できるかどうかを判断するために、サーバーがソケットと積極的に対話する必要があるかどうかを尋ねました。
さらに、PHP には connection_status と connection_aborted という 2 つのメソッドが用意されており、どちらも現在の接続ステータスを検出できることもわかりました。したがって、コードのログ行は次のように変更できます:
手動接続処理の表示に従って、現在の接続ステータスを印刷できます。
以下には、ソケットと対話するプログラムがありません。echo を使用します。フラッシュの影響を排除するために、後でフラッシュすることを忘れないでください。
プログラムは
に変更されます リーリー わかりました。前に作成したクライアントを実行します。観察記録:
ついに中止されました。ログには、今後数回の中止ステータスが 1 であることも示されています。
しかし、ここで何か奇妙なことがあります。最初の 2 つの接続ステータスのステータスが 0 (正常) のままなのはなぜですか。
RST
Wireshark を使用してパケットをキャプチャし、クライアントとサーバー間の対話プロセス全体を確認します
このプロセス全体では 14 パケットのみが送信されます。サーバーが初めて 22222 を送信したときに、クライアントが RST を返すことを見てみましょう。それ以降のパッケージ要求はありません。
クライアントとサーバー間のおおよその対話プロセスは次のとおりです。
サーバーがループで初めて 2222 を送信すると、クライアントは切断され、RST を返しますが、この送信プロセスは成功したリクエストとみなされます。サーバーが再度このソケットで書き込み操作を実行する必要があるときまで、このソケットはネットワーク送信を実行せず、接続ステータスが中止であることを直接返します。したがって、上記の状況が発生したのは最初の 222 のステータスが 0 で、中止が発生したのは 2 回目でした。
確認してください
strace php -S XXX を使用して検証することもできます
プロセス全体の strace ログは次のとおりです:
リーリー2222 がソケットに 2 回目に送信されると、壊れたパイプが表示されます。これは、このソケットが使用できなくなったことを知らせるプログラムです。ちなみに、php の connection_status は 1 に設定されます。後続の書き込み操作は再度実行されません。
概要
通常の状況では、クライアントが異常に起動された場合、サーバー プログラムは IO と 2 回対話するまで実行を続けます。この時点で、サーバーはクライアントが切断されたことを検出し、ignore_user_abort が設定されていない場合、php-fpm プログラムは中断されます。
この時点で、問題は解決されました。
ブラウザ終了後も PHP が実行を継続するかどうかに関する上記の詳細な分析は、編集者が共有したすべての内容です。参考にしていただければ幸いです。また、皆さんにも Bangkejia をサポートしていただければ幸いです。