|
netstat で報告されているように)でより多くの接続があるという報告があります。サーバが TCP の接続を閉じているときに、ACK ビットセットのパケットで応答するクライアントに対して、送られる FIN ビットセットでパケットを送信します。それからクライアントが ACK で応答するサーバに対して FIN ビットセットでパケットを送信して、接続は閉じられます。接続がサーバがクライアントから ACK を得て、サーバが FIN をクライアントから得るまでの間にあるステートが、FIN_WAIT_2 として知られています。ステートの移行についての技術的な詳細については、TCP RFC を見てください。
FIN_WAIT_2 のステートは、それについて標準で定義されているタイムアウトがあるという点で幾分変わっています。これはシステムがリブートされるまで、多くの OS で FIN_WAIT_2 ステートのコネクションが継続することを意味します。もしシステムがタイムアウトと、多すぎる接続を持っていなければ、接続についての情報を貯えるために割り当てられた余地を埋めて、カーネルを壊すことができます。FIN_WAIT_2 にある接続は httpd の処理に行き詰まりません。
幸運なら、バギーなクライアントは完全に接続を閉じて、サーバにリソースをリリースするでしょう。しかし、クライアントを閉じる前に、プロバイダからダイアルアップクライアントが切断するように、ソケットが全く閉じない場合があります。更に、クライアントは他の接続を作らないで数日間放置して、このように、そのための更なる使用がなくてもソケットのエンドを数日間開きっぱなしにするかもしれません。これは、ブラウザか、その OS の TCP の実行にあるバグです。
この問題が存在することが確かめられているクライアント:
これは問題が出ていません:
多くの他のクライアントが同じ問題を持っていることが予想されます。クライアントがすべき事は、もしサーバによってソケットが閉じられれば、周期的にその開いているソケットをチェックし、サーバが閉じていればソケット側の接続を閉じることです。このチェックは2、3秒毎に一度だけ生じる必要があり、いくつかのシステムでは OS シグナルによって検出されるかもしれません(例えば、Win95 と NT クライアントはこの可能性がありますが、それを無視しているように見えます)。
バギーなクライアントのための持続的な接続を不能にしなければ、ちょうど他のバグのせいで、Navigator 2.x クライアントのために行うことを推奨しているように、Apache はこれらの FIN_WAIT_2 ステートを避けることができません。しかしながら、非持続的な接続はクライアント毎に必要とされる接続の全体数を増やし、画像の載ったウェブページの検索を遅くします。非持続的な接続はリソースの消費と、それぞれの閉鎖の後の短い待機期間になるので、忙しいサーバはそのクライアントの最良の供給のために、持続性を必要とするかもしれません。
私たちが知る限りでは、クライアントに起因する FIN_WAIT_2 の問題は、Apache 1.1.x と 1.2 を含む持続的な接続をサポートしている全てのサーバに存在します。
lingering_close() と呼ばれる機能です。この機能は持続的な接続と、メッセージ本体にあるコンテンツを含んでいるリクエストの適切なハンドリングのために必要なものです(例えば、PUTs と POSTs)。それがすることは、サーバが接続を閉じた後の特定の時間にクライアントによって送信されたデータを読むことです。これを行う確かな理由はいくらか複雑ですが、もしサーバがレスポンスを送信して、接続を閉じるときにクライアントがリクエストを作れば、起こることは複雑になります。lingering なしで、クライアントはサーバのレスポンスを読む機会を持って、なぜ接続が閉じられたのかをこのように理解する前に、その TCP 入力バッファをリセットすることを強制されるかもしれません。詳しくはappendix を見てください。
lingering_close() にあるコードは、それが引き起こすトラフィックパターンにある変化を含んでいる、原因となる問題に因数の番号を求めます。コードは完全に解析され、私たちはその中にあるバグを気にしていません。FIN_WAIT_2 のためのタイムアウトが欠けているのとは別に、発見された問題の原因となっている lingering_close コードによってさらされている、BSD TCP スタックにいくつかの問題がある可能性があります。
tcp_fin_wait_2_flush_intervalを修正するために、nddを使うことによってチューニングされますが、デフォルトはほとんどのサーバに割り当てられ、不適切なチューニングはマイナスとなります。 SO_LINGERソケットオプションと関係します。このパラメータは、tcp_keepstart と tcp_keepstop のようなパラメータを修正するためのnettuneを使うことによって、調整されます。後のバージョンでは、修正できる FIN_WAIT_2 にある接続のためのはっきりしたタイマーがあります; 詳細についてはHPサポートと連絡を取ってください。
以下のシステムはタイムアウトを持っていないことが知られています:
FIN_WAIT_2 ステートにタイムテーブルを追加するために patch available があります; それは、もとは BSD/OS のためのものですが、BSD のネットワークコードを使うほとんどのシステムで使えるようにするべきでしょう。その使用を可能にするためには、カーネルソースコードが必要です。もし、他のシステムで動くようにするなら、marc@apache.org で私に注釈をください。
lingering_close() を使わないでコンパイルするlingering_close() の機能を使わないで Apache 1.2 をコンパイルすることが可能です。これは、1.1 にあるものと類似したコードのセクションになります。もしこうするなら、特にクライアントがパイプラインを使っていれば、PUTs 、POSTs、持続的な接続で問題が起こり得ることに気を付けてください。1.1 ほどひどくなく、サーバを走らせ続けるのは非常に大事だということです。
lingering_close() の機能なしでコンパイルするためには、Configuration ファイルの EXTRA_CFLAGS 行の終わりに -DNO_LINGCLOSE を追加して Configure を再び走らせ、サーバを再構築してください。
lingering_close() の代わりに SO_LINGER を使うsetsockopt(2) でセットされる SO_LINGER と呼ばれるオプションがあります。lingering_close よりも、ずっと問題を引き起こすことが多いような多くのシステムで壊されることを除いては、lingering_close() と非常に似たことをします。いくつかのシステムでは、代わりの方法がなければ、試す意味があるような方法で、できるだけうまく働くことができます。
それを試すためには Configuration の EXTRA_CFLAGS 行の最後に -DUSE_SO_LINGER -DNO_LINGCLOSE を追加して、Configure を再び走らせ、サーバを再構築してください。
NOTE: 同時に SO_LINGER と lingering_close() を使うことを試みるのは、非常に悪いことのようですが、そうではありません。
それらを増やす正確な方法は、OS に依存しているかもしれません; "mbufs" や "mbuf clusters" の数を参照してください。多くのシステムで、これは、カーネルコンフィグファイルに欲しい mbuf クラスターの数が n である、NMBCLUSTERS="n" 行を追加して、カーネルを再構築することによって行われます。
もし上のいくつかが行えないのであれば、最後の手段として KeepAlive を無効にすべきです。httpd.conf を編集して、"KeepAlive On" を "KeepAlive Off" に変えてください。
以下は HTTP/1.1 開発者の一人である Roy Fielding からのメッセージです。
もしクライアントがデータを送信している間(または、データの送信を計画している間)に、サーバが接続の入力側を閉じれば、サーバの TCP スタックはクライアントに RST (reset) の信号を返します。RST を受け取って、クライアントは RST パケット項によって示される un-ACKed パケットに それ自身の入ってくる TCP バッファを放出するでしょう。もし閉じる前にサーバがクライアントにメッセージ(普通はエラーのレスポンス)送信して、アプリケーションコードがその入って来る TCP バッファからエラーメッセージを読んで、サーバがそのバッファを受け取った上でクライアントによって送信された ACK を受け取る前に、クライアントが RST パケットを受け取ると、RST はクライアントアプリケーションがそれを見る機会を得る前にエラーメッセージを放出します。結果としてクライアントは明らかな理由もなく接続が失敗したという思いを残します。
これが起こりそうな、二つの状態があります:
全ての場合における解決策は、クライアントによって閉じられるか(最後にレスポンスを読むことを意味する)、タイムアウトが生じるかのどちらかまで、レスポンスを送信して接続の記録半分だけを閉じて(シャットダウンが行われることを前提とする)、ソケット上で読み続けることです。もし SO_LINGER が設定されていれば、それはカーネルが行うことを要求します。不幸にも SO_LINGER いくつかのシステムで効果がありません; システムのいくつかでは、次のリブート(予定されていようとなかろうと)までそれ自身のタイムアウトを持たず、このような TCP メモリセグメントはただ積み重なります。
単にリンガーコードを外すだけでは問題は解決しないことに注意してください -- ただそれを異なったものにして、検出を困難にします。
The English original manual is here.