2011-04-16 55 views
6

我试图编写一个快速的HTML刮板,此时我只关注最大化吞吐量而不解析。我已缓存的URL的IP地址:在Java中获取多个网页的最快方法

public class Data { 
    private static final ArrayList<String> sites = new ArrayList<String>(); 
    public static final ArrayList<URL> URL_LIST = new ArrayList<URL>(); 
    public static final ArrayList<InetAddress> ADDRESSES = new ArrayList<InetAddress>(); 

    static{ 
     /* 
     add all the URLs to the sites array list 
     */ 

     // Resolve the DNS prior to testing the throughput 
     for(int i = 0; i < sites.size(); i++){ 

      try { 
       URL tmp = new URL(sites.get(i)); 
       InetAddress address = InetAddress.getByName(tmp.getHost()); 
       ADDRESSES.add(address); 
       URL_LIST.add(new URL("http", address.getHostAddress(), tmp.getPort(), tmp.getFile())); 
       System.out.println(tmp.getHost() + ": " + address.getHostAddress()); 
      } catch (MalformedURLException e) { 
      } catch (UnknownHostException e) { 
      } 
     } 
    } 
} 

我的下一个步骤是通过从互联网上获取他们,看完第一64KB和移动到下一个URL来测试与100个网址的速度。我创建的FetchTaskConsumer的一个线程池,我已经试过(一i7四核机上16〜64)运行多个线程,这里是每个消费者的外观:

public class FetchTaskConsumer implements Runnable{ 
    private final CountDownLatch latch; 
    private final int[] urlIndexes; 
    public FetchTaskConsumer (int[] urlIndexes, CountDownLatch latch){ 
     this.urlIndexes = urlIndexes; 
     this.latch = latch; 
    } 

    @Override 
    public void run() { 

     URLConnection resource; 
     InputStream is = null; 
     for(int i = 0; i < urlIndexes.length; i++) 
     { 
      int numBytes = 0; 
      try {     
       resource = Data.URL_LIST.get(urlIndexes[i]).openConnection(); 

       resource.setRequestProperty("User-Agent", "Mozilla/5.0"); 

       is = resource.getInputStream(); 

       while(is.read()!=-1 && numBytes < 65536) 
       { 
        numBytes++; 
       } 

      } catch (IOException e) { 
       System.out.println("Fetch Exception: " + e.getMessage()); 
      } finally { 

       System.out.println(numBytes + " bytes for url index " + urlIndexes[i] + "; remaining: " + remaining.decrementAndGet()); 
       if(is!=null){ 
        try { 
         is.close(); 
        } catch (IOException e1) {/*eat it*/} 
       } 
      } 
     } 

     latch.countDown(); 
    } 
} 

充其量我能在大约30秒内浏览100个URL,但文献表明我应该能够通过每秒150个URL。请注意,我可以访问千兆位以太网,尽管目前我正在家中使用我的20 Mbit连接运行测试......无论如何,连接永远不会被充分利用。

我试过直接使用Socket连接,但是我一定是做错了,因为那样会更慢!有关如何提高吞吐量的任何建议?

P.S.
我有一个约100万热门网址的列表,因此如果100不足以进行基准测试,我可以添加更多网址。

更新:
literature I'm referring是与Najork网络爬虫的论文,Najork规定:

在17天
被处理的8.91亿的网址〜606个下载每秒[上] 4 Compaq DS20E Alpha服务器[含] 4 GB主内存[,] 650 GB磁盘空间[和] 100 MBit/sec。
以太网ISP速率限制带宽 160Mbits /秒

所以它实际上是每秒150页,而不是300.我的电脑是酷睿i7与4 GB的RAM和我隔靴搔痒接近。我没有看到任何说明他们特别使用的东西。

更新:
好吧,总结起来......最后的结果在!事实证明,100个URL对于基准来说有点太低。我碰到了1024个URL,64个线程,我为每次获取设置了2秒的超时时间,并且我能够达到每秒21页(实际上,我的连接大约为10.5 Mbps,因此每秒21页* 64KB每页大约10.5 Mbps)。这里是fetcher的样子:

public class FetchTask implements Runnable{ 
    private final int timeoutMS = 2000; 
    private final CountDownLatch latch; 
    private final int[] urlIndexes; 
    public FetchTask(int[] urlIndexes, CountDownLatch latch){ 
     this.urlIndexes = urlIndexes; 
     this.latch = latch; 
    } 

    @Override 
    public void run() { 

     URLConnection resource; 
     InputStream is = null; 
     for(int i = 0; i < urlIndexes.length; i++) 
     { 
      int numBytes = 0; 
      try {     
       resource = Data.URL_LIST.get(urlIndexes[i]).openConnection(); 

       resource.setConnectTimeout(timeoutMS); 

       resource.setRequestProperty("User-Agent", "Mozilla/5.0"); 

       is = resource.getInputStream(); 

       while(is.read()!=-1 && numBytes < 65536) 
       { 
        numBytes++; 
       } 

      } catch (IOException e) { 
       System.out.println("Fetch Exception: " + e.getMessage()); 
      } finally { 

       System.out.println(numBytes + "," + urlIndexes[i] + "," + remaining.decrementAndGet()); 
       if(is!=null){ 
        try { 
         is.close(); 
        } catch (IOException e1) {/*eat it*/} 
       } 
      } 
     } 

     latch.countDown(); 
    } 
} 
+0

为刮板设置浏览器useragent是不是**良好的做法。 – Mat 2011-04-16 17:36:34

+0

文学?你的意思是说javadocs?我找不到与URLConnection相关的每秒300个URL的信息。 – Babar 2011-04-16 17:44:56

+0

URLConnection大多每500ms获得一个页面,java在这个目的上很慢 – 2011-04-16 17:59:36

回答

2

你确定你的款项?每秒

300的URL,每个URL读取64千字节

这需要:300×64 = 19,200千字节/秒

转换为位:19200千字节/秒=(8 * 19200)千比特/秒

因此,我们有:8 * 19200 = 153600千比特/秒

然后到Mb/s的:153600/1024 = 150兆比特/秒

...然而你只有一个20 Mb/s的频道。

不过,我想你提取的很多网址的大小都在64Kb以下,因此通过速度比你的频道快。你不慢,你快!

+0

即使在20 MB/s(我的家庭连接),我应该至少能够以每秒40页的速度将其最大化......我距离那么近(在30秒内100个URL,大约每页3页第二)!我也可以访问100 Mbps的连接,但是如果我可以在达到100 Mbps之前最大限度地连接家庭连接,我会很高兴。 – Kiril 2011-04-16 18:15:28

+0

我的歉意 - 我更关注你的期望,而不是你的成就。我会看到我能弄清楚的。 – 2011-04-16 19:34:14

+0

这是一个很好的观点,即使它没有真正回答我的问题:)。现在我正在关注Nutch,以及他们如何设法获取如此多的URL,而不会遇到和我一样的问题。也许他们没有使用'URLConnection' ... – Kiril 2011-04-16 20:16:29

1

这次专注于你的成就。我自己试着使用你的代码,发现我每秒钟访问的主要站点大约有3页。如果我访问我自己的网络服务器下载静态页面,但是,我最大化我的系统。

今天在互联网上,一个主要网站通常需要一秒多的时间来生成一个页面。查看了他们刚刚发送给我的数据包后,该页面即将到达多个TCP/IP数据包。从英国这里,下载www.yahoo.co.jp需要3秒钟,下载amazon.com需要2秒,但facebook.com只需要不到0.1秒。区别在于facebook.com首页是静态的,而另外两个是动态的。对于人类来说,关键因素是第一个字节的时间,这是浏览器可以开始做什么的时间,而不是第65536字节的时间。没有人优化:-)

那么这对你意味着什么?当你专注于热门网页时,我想你也将注意力集中在动态页面上,而这些页面并不像静态页面那样快。由于我查看的站点正在为它们的页面发送多个数据包,这意味着如果您同时获取多个页面,则数据包可能会在以太网上相互碰撞。

当两个网站同时向您发送数据包时会发生数据包冲突。在某些时候,来自两个网站的输入必须与电脑的单线协调。当两个数据包相互叠加时,组合它们的路由器会拒绝这两个数据包,并指示两个发送者在不同的短延迟后重新发送。实际上,这会降低这两个网站的速度。

因此:

1)这些页面的生成速度并不快。 2)以太网难以应对多个同时下载。 3)静态网站(过去比较常见)比动态网站速度更快,使用的数据包更少。

这一切都意味着最大化您的连接是非常困难的。

你可以尝试一下我做的1000个64Kb文件并看看你的代码能够下载它们的速度。对我来说,你的代码工作得很好。

+0

Simon,我将批量增加到了300个URL,我在60秒内得到了它们......所以我越来越好:每秒5个URL。我参考的文献也是读取第一个64KB。 Nutch似乎也做得很好:每秒40页。现在,我将我的一批网址设置为1000,以查看是否还有性能提升。 – Kiril 2011-04-16 22:39:05

+0

我现在增加了1024个URL,并且使用相同的代码,现在每秒可以达到21页(在〜47秒内提取1024个)。 – Kiril 2011-04-16 23:38:40

相关问题