私はJava Webクローラを持っています。私はなぜ把握望めないクローラはいくつかのサーバに多くのESTABLISHED TCPソケットを残します
[email protected]:~/tmp/test$ lsof -p 6760 | grep TCP
java 6760 joel 105u IPv6 96546 0t0 TCP bohr:55602->174.143.223.193:www (ESTABLISHED)
java 6760 joel 109u IPv6 96574 0t0 TCP bohr:55623->174.143.223.193:www (ESTABLISHED)
java 6760 joel 110u IPv6 96622 0t0 TCP bohr:55644->174.143.223.193:www (ESTABLISHED)
java 6760 joel 111u IPv6 96674 0t0 TCP bohr:55665->174.143.223.193:www (ESTABLISHED)
いずれかのサーバー&にこれらの数十があるかもしれません:私は私が確立ソケットの多数を残していますクロールサーバの数が少ないためという私が気づきました彼らは開いたままです。
私はHttpURLConnection
を使用して接続を確立し、データを読み取ります。 HTTP 1.1およびkeep-alive
はオンです(デフォルト)。入力/エラーストリームを閉じ、すべてのデータがストリームから読み込まれている限り、リモートサーバーへの基底のtcpソケットはJavaのHttpURLConnection
によって再利用されることは私の理解です。また、例外がスローされた場合、入力/エラーストリームが閉じられている限り(nullでない場合)、ソケットは再び再利用されませんが、閉じられます。 (java handling of http-keepalive)
私の省略コードは次のようになります。
:InputStream is = null;
try {
HttpURLConnection conn = (HttpURLConnection) uri.toURL().openConnection();
conn.setReadTimeout(10000);
conn.setConnectTimeout(10000);
conn.setRequestProperty("User-Agent", userAgent);
conn.setRequestProperty("Accept", "text/html,text/xml,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
conn.setRequestProperty("Accept-Encoding", "gzip deflate");
conn.setRequestProperty("Accept-Language", "en-gb,en;q=0.5");
conn.connect();
try {
int responseCode = conn.getResponseCode();
is = conn.getInputStream();
} catch (IOException e) {
is = conn.getErrorStream();
if (is != null){
// consume the error stream, http://download.oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html
StreamUtils.readStreamToBytes(is, -1 , MAX_LN);
}
throw e;
}
String type = conn.getContentType();
byte[] response = StreamUtils.readStream(is);
// do something with content
} catch (Exception e) {
conn.disconnect(); // don't try to re-use socket - just be done with it.
throw e;
} finally {
if (is != null) {
is.close();
}
}
私は、これが起こっているサイトのために私が起因して、GET要求を行う場合にスローさIOExceptionsの多くを得ることに気付きました
java.net.ProtocolException: Server redirected too many times (20)
私はこれを処理していると確信しています。ソケットを正しく閉じています。それは本当にこれ、または私が間違っている何か他のものか?それはキープアライブを誤って使用した結果かもしれません。むしろ問題を解決するためにキープ・アライブをオフにする必要はありません。
EDIT:私は次のプロパティを設定しテストしてみた:
conn.setRequestProperty("Connection", "close"); // supposed to disable keep-alive
Connection: close
ヘッダは持続的なTCP接続を無効にし、すべてのソケットが最終的にクリーンアップされて送信します。だから、私が見ている問題は実際には入力ストリームを閉じた後でさえ、keep-alive
とソケットが正しく閉じられていないことと思われます。
EDIT2 - リクエストがリダイレクトされるたびに1つのソケットが作成される可能性がありますか?この問題が顕著な場合は、上記の例外がスローされる前に要求が20回リダイレクトされます。この場合、URLConnectionのリダイレクト数を制限する方法はありますか?
私は故意に最後から切断を残しました。切断を呼び出すと、基礎となるtcpソケットを再利用することができなくなります(キープアライブを使用する点が無効になります)。私が見ているのは、同じサーバーに対して、同じソケットが別のリクエスト(および別のURLConnectionインスタンス)で再利用されていることです。処理中に例外が発生した場合にのみ切断を呼び出すか、すべてのコンテンツを読み取ることができません(ソケットは再利用できないため)。 – Joel