以下の文書は、http://wiki.apache.org/jakarta-httpclient/FrequentlyAskedConnectionManagementQuestionsの内容の一部を翻訳したものです。 TIME_WAIT状態のコネクション TIME_WAIT状態 アプリケーションを走らせた後に、netstatコマンドを使用します。 すると、たくさんのTIME_WAIT状態が見つかります。 ここで、あなたはなぜこれらのコネクションがクリーンアップされないか疑問を持つでしょう。 TIME_WIAT状態とは何か TIME_WAIT状態は、TCPの保護機能です。 ソケットを閉じる側は一般的に1〜4分TIME_WAIT状態でコネクションを保ちます。 これはコネクションを閉じた後に起こり、クリーンアップの問題ではありません。 TIME_WAIT状態はデータの損失と破損を防ぎます。 技術的な詳細は、Unix Socket FAQ, section 2.7を参照して下さい。 どのコネクションがTIME_WAITになって、他がならないか もし、コネクションがアプリケーションによって正しく閉じられるなら、TIME_WAIT状態になります。 コネクションがサーバで正しく閉じられるなら、サーバはTIME_WAIT状態を保ちクライアントはそうではありません。 結合がリセットされるか、正しくないやり方でアプリケーションが落とされるなら、TIME_WAIT状態とはなりません。 残念なことに、コネクションが正しく閉じられるかどうかは、あなたにとって必ずしも明らかではありません。 これはコネクションがプールされてデフォルトで再利用のために開いておかれるからです。 HttpClient 3.x、HttpClient 4、さらに標準的なJava HttpURLConnetionはそうします。 大部分のアプリケーションは単にリクエストを実行し、それからレスポンスのストリームから読み込みます。 そして、最後にストリームを閉じます。 レスポンスのストリームを閉じることは、コネクションを閉じることと同じではありません。 レスポンスのストリームを閉じると、プールにコネクションを返します。 しかし、それは可能ならば開いておかれます。 別のリクエストを同じホストに2、3秒(分)以内に送るなら、これは多くの時間を節約します。 コネクションプールにはコネクション数の限界があります。 プールは5、または100、もしくは1つだけのコネクションを持つかもしれません。 ホストにリクエストを送信し、ホストへの開いたコネクションがプールにない時、新しいコネクションを開く必要があります。 しかし、もしプールが既にいっぱいである場合は、新しいコネクションが開かれる前に、開いたコネクションは閉じられなければいけません。 この場合は、古いコネクションは正しく閉じられ、TIME_WAIT状態となります。 アプリケーションが終了してJVMが終わるとき、プールの開いたコネクションは正しく閉じられないでしょう。 それらはTIME_WAIT状態になることなく、リセットかキャンセルされます。 これを避けるために、アプリケーションが使っているコネクションプールのshutdownメソッドを終了する前に呼ばなければいけません。 標準的なHttpURLConnectionは、コネクションプールをshutdownする公開メソッドがありません。 ポート数の使い果たし アプリケーションには、たくさんのコネクションを短時間で開いて閉じるものがあります。 例えばサーバの負荷テストする時などです。 TIME_WAIT状態でコネクションは、そのポート番号が他のコネクションのために再利用されることを防ぎます。 これはエラーではありません。 TIME_WAITの目的です。 TCPは、JAVAを通してではなくOSレベルで設定されます。 最初の行動としては、マシンでの短命なポート数の増加するべきです。 特にWindowsは短命なポート数において低いデフォルト値を持ちます。 PerformanceWikiには、一般的なOSチューニングのTIPSがあります。 それぞれにのTIPSにはNetworkのセクションがあります。 短命なポート数を増やすことが問題を解決しない時だけ、TIME_WAIT状態の持続時間を減らすことを考えるべきです。 おそらく一般的に2度の遅延時間を考慮にいれた時間であるTIME_WAITの持続時間として、IPパケットの最大有効期間を減らさなければならないでしょう。 これはマシンで動いている全てのアプリケーションに影響を及ぼすことに注意して下さい。 方法を私たちに聞かないでください。私たちはネットワークチューニングの専門家ではありません。 アプリケーションレベルで問題を扱う若干の方法があります。 1つの方法は、各リクエストで「Connection: close」ヘッダを送ることです。 それはサーバにコネクションを閉じるように言うので、向こう側でTIME_WAIT状態になります。 もちろんこれはプールしているコネクションのkeep-aliveの特徴を無効にし、それによってパフォーマンスを低下させます。 もしサーバに対して負荷テストをしているなら、アプリケーションの一般的でない動作は、試験結果をゆがめるかもしれません。 別の方法はコネクションを正しく閉じないことです。 コネクションを正しく閉じる代わりにリセットさせる特別な値としてSO_LINGERを設定する方法があります。 HttpClient APIは直接これをサポートしていないことに注意して下さい。 このハックを実装するためにいくつかのクラスを拡張や修正する必要があります。 さらに別の方法は、TIME_WAITのコネクションによってまだふさがれているポートを再利用することです。 ソケットを開くとき、SO_REUSEADDRオプションを指定することによって可能です。 Java1.4はこの目的のためにSocket.setReuseAddressメソッドを導入しました。 これでも、HttpClientのクラスを拡張・修正する必要があります。 しかし、少なくともそれはハックではありません。 |