【发布时间】:2011-01-23 12:56:05
【问题描述】:
我有一个 Java 网络爬虫。我注意到,对于我抓取的少量服务器,我留下了大量 ESTABLISHED 套接字:
joel@bohr:~/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 处于打开状态(默认情况下)。据我了解,Java 的HttpURLConnection 将重新使用远程服务器的底层 tcp 套接字,只要我关闭输入/错误流,并从流中读取所有数据。我的理解也是,如果抛出异常,那么只要输入/错误流被关闭(如果不是 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)
我很确定我正在处理这个问题,正确地关闭了套接字。真的是这样,还是我做错了什么?这可能是误用 keep-alive 的结果 - 如果是这样,如何解决?我宁愿不必关闭keep-alive来解决问题。
编辑:我已测试设置以下属性:
conn.setRequestProperty("Connection", "close"); // supposed to disable keep-alive
发送Connection: close 标头会禁用持久 tcp 连接,并且所有套接字最终都会被清除。所以,我看到的问题似乎确实与keep-alive 和套接字没有正确关闭有关,即使在关闭输入流之后也是如此。
EDIT2 - 每次重定向请求时都会创建一个套接字吗?在这个问题很明显的地方,请求在抛出上述异常之前被重定向了 20 次。如果是这种情况,是否有办法限制 URLConnection 上的重定向次数?
【问题讨论】:
标签: java url network-programming web-crawler