【问题标题】:Crawler leaves lots of ESTABLISHED TCP sockets to some serversCrawler 将大量已建立的 TCP 套接字留给某些服务器
【发布时间】: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


    【解决方案1】:

    您需要将conn.disconnect() 移动到您的finally 部分。因为只有在抛出异常时才断开连接。

    【讨论】:

    • 我故意将断开连接排除在最后。当您调用断开连接时,不能再重新使用底层 tcp 套接字(这会破坏使用保持活动的意义)。我看到的是,对于大多数服务器,我抓取的相同套接字在不同请求(和不同 URLConnection 实例)中重复使用到同一服务器 - 到目前为止一切都很好。我只在处理过程中出现异常或无法读取所有内容时才调用断开连接(因为无论如何都无法重新使用套接字)。
    猜你喜欢
    • 1970-01-01
    • 2014-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-21
    • 2017-03-17
    • 2013-05-15
    相关资源
    最近更新 更多