[an error occurred while processing this directive]
[APACHE DOCUMENTATION]

Apache HTTP Server Version 1.3

FIN_WAIT_2 ステートでの接続と Apache

  1. FIN_WAIT_2 ステートとは?

    Apache 1.2 ベータで起動すると、古いバージョンを使っているよりも FIN_WAIT_2 ステート(netstat で報告されているように)でより多くの接続があるという報告があります。サーバが TCP の接続を閉じているときに、ACK ビットセットのパケットで応答するクライアントに対して、送られる FIN ビットセットでパケットを送信します。それからクライアントが ACK で応答するサーバに対して FIN ビットセットでパケットを送信して、接続は閉じられます。接続がサーバがクライアントから ACK を得て、サーバが FIN をクライアントから得るまでの間にあるステートが、FIN_WAIT_2 として知られています。ステートの移行についての技術的な詳細については、TCP RFC を見てください。

    FIN_WAIT_2 のステートは、それについて標準で定義されているタイムアウトがあるという点で幾分変わっています。これはシステムがリブートされるまで、多くの OS で FIN_WAIT_2 ステートのコネクションが継続することを意味します。もしシステムがタイムアウトと、多すぎる接続を持っていなければ、接続についての情報を貯えるために割り当てられた余地を埋めて、カーネルを壊すことができます。FIN_WAIT_2 にある接続は httpd の処理に行き詰まりません。

  2. でも、なぜそうなるのですか?

    それが起こる理由はたくさんあり、そのいくつかはまだはっきりしません。以下のことがわかっています。

    バギーなクライアントと持続的な接続

    いくつかのクライアントは persistent connections(keepalives) を扱うときにポップアップするバグを持っています。接続がアイドリングしていて、サーバが接続を閉じているとき(KeepAliveTimeout を基にして)、クライアントはサーバに FIN と ACK を返送しないようにプログラムされています。これは以下のうちの一つが起こるまで、FIN_WAIT_2 ステートで接続が継続することを意味します:

    幸運なら、バギーなクライアントは完全に接続を閉じて、サーバにリソースをリリースするでしょう。しかし、クライアントを閉じる前に、プロバイダからダイアルアップクライアントが切断するように、ソケットが全く閉じない場合があります。更に、クライアントは他の接続を作らないで数日間放置して、このように、そのための更なる使用がなくてもソケットのエンドを数日間開きっぱなしにするかもしれません。これは、ブラウザか、その OS の TCP の実行にあるバグです。

    この問題が存在することが確かめられているクライアント:

    これは問題が出ていません:

    多くの他のクライアントが同じ問題を持っていることが予想されます。クライアントがすべき事は、もしサーバによってソケットが閉じられれば、周期的にその開いているソケットをチェックし、サーバが閉じていればソケット側の接続を閉じることです。このチェックは2、3秒毎に一度だけ生じる必要があり、いくつかのシステムでは OS シグナルによって検出されるかもしれません(例えば、Win95 と NT クライアントはこの可能性がありますが、それを無視しているように見えます)。

    バギーなクライアントのための持続的な接続を不能にしなければ、ちょうど他のバグのせいで、Navigator 2.x クライアントのために行うことを推奨しているように、Apache はこれらの FIN_WAIT_2 ステートを避けることができません。しかしながら、非持続的な接続はクライアント毎に必要とされる接続の全体数を増やし、画像の載ったウェブページの検索を遅くします。非持続的な接続はリソースの消費と、それぞれの閉鎖の後の短い待機期間になるので、忙しいサーバはそのクライアントの最良の供給のために、持続性を必要とするかもしれません。

    私たちが知る限りでは、クライアントに起因する FIN_WAIT_2 の問題は、Apache 1.1.x と 1.2 を含む持続的な接続をサポートしている全てのサーバに存在します。

    1.2 で紹介された必要なビットコード

    上記のバグは問題とはなりますが、全体の問題ではありません。Apache 1.1.x についてはユーザは FIN_WAIT_2 の問題を見ていませんが、1.2b では十分な接続が、サーバを壊す FIN_WAIT_2 ステートで強まります。追加の FIN_WAIT_2 ステートで最も可能性の高いソースは、1.1 と 1.2 で追加された lingering_close() と呼ばれる機能です。この機能は持続的な接続と、メッセージ本体にあるコンテンツを含んでいるリクエストの適切なハンドリングのために必要なものです(例えば、PUTs と POSTs)。それがすることは、サーバが接続を閉じた後の特定の時間にクライアントによって送信されたデータを読むことです。これを行う確かな理由はいくらか複雑ですが、もしサーバがレスポンスを送信して、接続を閉じるときにクライアントがリクエストを作れば、起こることは複雑になります。lingering なしで、クライアントはサーバのレスポンスを読む機会を持って、なぜ接続が閉じられたのかをこのように理解する前に、その TCP 入力バッファをリセットすることを強制されるかもしれません。詳しくはappendix を見てください。

    lingering_close() にあるコードは、それが引き起こすトラフィックパターンにある変化を含んでいる、原因となる問題に因数の番号を求めます。コードは完全に解析され、私たちはその中にあるバグを気にしていません。FIN_WAIT_2 のためのタイムアウトが欠けているのとは別に、発見された問題の原因となっている lingering_close コードによってさらされている、BSD TCP スタックにいくつかの問題がある可能性があります。

  3. それについてなにができますか?
  4. 他よりよく働く、問題に対処するためのいくつかの次善策があります。

    FIN_WAIT_2 のためにタイムアウトを追加する

    わかりきった次善策は、単に FIN_WAIT_2 ステートのためにタイムアウトを持つことです。これは RFC による指定が行われず、RFC の違反であることを言っていますが、必然的な事として広く認識されています。以下のシステムはタイムアウトを持っていることが知られています:

    以下のシステムはタイムアウトを持っていないことが知られています:

    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() と非常に似たことをします。いくつかのシステムでは、代わりの方法がなければ、試す意味があるような方法で、できるだけうまく働くことができます。

    それを試すためには ConfigurationEXTRA_CFLAGS 行の最後に -DUSE_SO_LINGER -DNO_LINGCLOSE を追加して、Configure を再び走らせ、サーバを再構築してください。

    NOTE: 同時に SO_LINGERlingering_close() を使うことを試みるのは、非常に悪いことのようですが、そうではありません。

    接続ステートをストアするために使われるメモリの量を増やす

    ネットワークコードを基にした BSD:
    mbuf と呼ばれるようものには、接続ステーツのようなネットワークデータを BSD がため込んでいます。カーネルがそれらを入れるのに十分な mbufs を持っていないような、多過ぎる接続を持つと、カーネルはクラッシュする可能性が高いです。利用できる mbufs の数を増やすことによって問題の影響を減らすことができます; これは問題を防ぎはせず、クラッシュする前にサーバがより長く動くようにします。

    それらを増やす正確な方法は、OS に依存しているかもしれません; "mbufs" や "mbuf clusters" の数を参照してください。多くのシステムで、これは、カーネルコンフィグファイルに欲しい mbuf クラスターの数が n である、NMBCLUSTERS="n" 行を追加して、カーネルを再構築することによって行われます。

    KeepAlive を無効にする

    もし上のいくつかが行えないのであれば、最後の手段として KeepAlive を無効にすべきです。httpd.conf を編集して、"KeepAlive On" を "KeepAlive Off" に変えてください。

  5. フィードバック
  6. このページに追加するような情報がありましたら、marc@apache.org で私にコンタクトをとってください。

  7. 追記
  8. 以下は HTTP/1.1 開発者の一人である Roy Fielding からのメッセージです。

    なぜ lingering 終了機能が HTTP で必要なのですか

    閉じた後でソケットに残るサーバに必要なのは、HTTP スペックで2倍であると書かれていますが、説明はされていません。この説明は私が W3C にいたときに MIT の廊下で私と、Henrik Frystyk、 Robert S. Thau、Dave Raggett、John C. Mallery で話し合ったことを基にしています。

    もしクライアントがデータを送信している間(または、データの送信を計画している間)に、サーバが接続の入力側を閉じれば、サーバの TCP スタックはクライアントに RST (reset) の信号を返します。RST を受け取って、クライアントは RST パケット項によって示される un-ACKed パケットに それ自身の入ってくる TCP バッファを放出するでしょう。もし閉じる前にサーバがクライアントにメッセージ(普通はエラーのレスポンス)送信して、アプリケーションコードがその入って来る TCP バッファからエラーメッセージを読んで、サーバがそのバッファを受け取った上でクライアントによって送信された ACK を受け取る前に、クライアントが RST パケットを受け取ると、RST はクライアントアプリケーションがそれを見る機会を得る前にエラーメッセージを放出します。結果としてクライアントは明らかな理由もなく接続が失敗したという思いを残します。

    これが起こりそうな、二つの状態があります:

    1. 適切な権限の委譲なしで POST か PUT のデータを送信すること
    2. それぞれのレスポンス(pipelining)と一つのエラーにあるミドルリクエストや他の break-the-connection となるものの前に多様なリクエストを送信すること

    全ての場合における解決策は、クライアントによって閉じられるか(最後にレスポンスを読むことを意味する)、タイムアウトが生じるかのどちらかまで、レスポンスを送信して接続の記録半分だけを閉じて(シャットダウンが行われることを前提とする)、ソケット上で読み続けることです。もし SO_LINGER が設定されていれば、それはカーネルが行うことを要求します。不幸にも SO_LINGER いくつかのシステムで効果がありません; システムのいくつかでは、次のリブート(予定されていようとなかろうと)までそれ自身のタイムアウトを持たず、このような TCP メモリセグメントはただ積み重なります。

    単にリンガーコードを外すだけでは問題は解決しないことに注意してください -- ただそれを異なったものにして、検出を困難にします。


Apache HTTP Server Version 1.3

検索文字
Index Home The English original manual is here.
このページの情報に関わる、ご質問、お問い合わせは、 japache@infoscience.co.jpまで。

JAPACHE ホームページ