2011-04-16 12 views
6

Hızlı bir HTML kazıyıcı yazmayı deniyorum ve bu noktada yalnızca ayrıştırma yapmadan verimimi en üst düzeye çıkarmaya odaklanıyorum. Ben URL'lerin IP adreslerini önbelleğe adres:Java'da birden çok web sayfası almanın en hızlı yolu

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) { 
      } 
     } 
    } 
} 

sonraki adımım, internetten şekilde almayı ilk 64KB okuma ve sonraki URL'ye hareket ettirerek 100 URL'yi hızını test etmekti. En iyi ihtimalle ben açabiliyorum

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(); 
    } 
} 

: Her tüketici böyle görünüyor burada, ben FetchTaskConsumer 's bir iş parçacığı havuzu oluşturmak ve ben (bir i7 Dört Çekirdekli makinede 64'e 16), birden konuları çalıştıran denedim Yaklaşık 30 saniyede 100 URL'den geçmek, ancak literatür, saniyede 150 URL'sini kullanabilmem gerektiğini önerir. Gigabit Ethernet'e erişebileceğimi, ancak şu anda 20 Mbit bağlantımda evde testi çalıştırdığım halde, bağlantının hiçbir zaman tam olarak kullanılmadığını unutmayın.

Doğrudan Socket bağlantılarını kullanarak denedim, ancak yanlış bir şeyler yapmalıyım, çünkü bu daha da yavaş! Verimi nasıl geliştirebileceğime dair herhangi bir öneriniz var mı?

P.S.
Yaklaşık 1 milyon popüler URL’den oluşan bir listeye sahibim. Bu nedenle, 100, ölçütleme için yeterli değilse daha fazla URL ekleyebilirim.

Güncelleme:
literature I'm referring Najork Web Paletli ilişkin belgeler olduğu, Najork devletler: ~ 606 indirme Saniyede [üzerinde] şeklindedir 17 Gün
yılında

İşlenmiş 891000000 URL'leri 4 Compaq DS20E Alpha Sunucular [with] 4 GB ana bellek [,] 650 GB disk alanı [ve] 100 MBit/sn.
Ethernet ISP hız limitleri bant genişliği için 160Mbits/sn

Demek ki 300. bilgisayarım 4 GB RAM ile Core i7 ve ben o hiçbir yerde yakın yakınım, aslında saniyede 150 sayfa. Özellikle ne kullandıklarını belirten bir şey görmedim.

Güncelleme:
Tamam, yukarı taksitli ... nihai sonuçları geldi! 100 URL’nin bir kıyaslama için biraz düşük olduğu ortaya çıkıyor. Ben 1024 URL, 64 iş parçacığı için çarptı, her getirme için 2 saniye bir zaman aşımı ayarladım ve saniyede 21 sayfaya varabildim (aslında benim bağlantım yaklaşık 10.5 Mbps, yani saniyede 21 sayfa * 64KB sayfa başına yaklaşık 10.5 Mbps'dir. Alıcının şu şekilde görünmesi:

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

Bir kazıyıcı için bir tarayıcı ayarlayıcısı kurma ** iyi bir uygulama değildir. – Mat

+0

Edebiyat? Javadocs demek mi istiyorsun? URLConnection ile ilgili saniyede 300 URL hakkında bir şey bulamıyorum. – Babar

+0

URLConnection çoğunlukla 500ms başına bir sayfa alır, bu amaç için java oldukça yavaştır –

cevap

2

Toplam miktarınızdan emin misiniz?300 x 64 = 19200 kilo byte/bit dönüştürme

ikinci: 19,200 kilo byte/saniye (= 8 * 19,200) saniyede

300 URL'ler, her bir URL, kilo byte gerektirir

64 okuma

ikinci kilo bit/Bu yüzden vardır: 153600/1024 = 150 mega bit/saniye

01:

Sonra

Mb/s'ye ikinci 8 * 19200 = 153600 kilo bit/... ve henüz sadece 20 Mb/s kanalınız var.

Ancak, aldığınız URL'lerin çoğunun 64 Kb boyutunun altında olduğunu düşünürsünüz. Bu nedenle, tüm satırlar kanalınızdan daha hızlı görünür. Yavaş değilsin, hızlısın!

+0

bile 20 MB/s'de (ev bağlantım) En azından saniyede 40 sayfalık bir sayıya ulaşabilmem gerekiyor ... Buna yakın bir yere yakın değilim (30 saniyede 100 URL, yaklaşık 3 sayfa ikinci)! Ayrıca 100 Mbps bağlantıya erişebiliyorum, ancak 100 Mbps'ye gitmeden önce ev bağlantımı maksimum hale getirebilirsem memnun olurum. – Kiril

+0

Özür dilerim - Beklentilerinize, başarılarınızdan daha çok odaklandım. Ne anlayabileceğimi göreceğim. –

+0

iyi bir nokta, daha az… soruyu gerçekten cevaplamıyor bile olsa :). Şimdi Nutch’a bakıyorum ve bu kadar çok URL’yi benim gibi aynı soruna girmeden nasıl getireceklerini düşünüyorum. Belki de "URLConnection" kullanmıyorlar ... – Kiril

1

Bu kez başarılarınız üzerinde duruluyor. Kodunuzu kendim kullanmaya çalıştım ve ayrıca büyük sitelere erişmede saniyede yaklaşık 3 sayfama sahip olduğumu keşfettim. Ancak kendi web sunucuma statik sayfaları indirirken eriştiysem, sistemimi maksimuma çıkardım.

Bugün internette, büyük bir site genellikle bir sayfa oluşturmak için saniyeden uzun sürüyor. Şimdi gönderdikleri paketlere bakıldığında, sayfa çoklu TCP/IP paketlerine ulaşıyor. Buradan İngiltere'de, amazon.com indirmek için www.yahoo.co.jp, 2 saniye indirmek için 3 saniye sürer, ancak facebook.com 0.1 saniyeden daha az sürer. Aradaki fark, facebook.com ön sayfasının statik, diğer ikisinin de dinamik olmasıdır. İnsanlar için kritik faktör, ilk bayt zamanıdır, bu, tarayıcının bir şey yapmaya başlayacağı zamandır, 65536 baytlık zamanı değil. Kimse bu kadarı optimize edemez?

Bu sizin için ne anlama geliyor? Popüler sayfalara odaklandığınız için, statik sayfalar kadar hızlı gönderilmeyen dinamik sayfalara odaklandığınızı hayal ediyorum. Baktığım siteler sayfalar için birden fazla paket gönderiyorlar, yani aynı anda birçok sayfa alıyorsanız ve bu nedenle paketler ethernet üzerinde birbirine çarpabilir.

İki web sitesi size aynı anda bir veri paketi gönderdiğinde paket çarpması gerçekleşir. Bir noktada, iki web sitesinden gelen girdinin bilgisayarınıza tek kabloyla eşgüdümlü olması gerekir. İki paket birbirinin üzerine geldiğinde, onları birleştiren yönlendirici her ikisini de reddeder ve iki göndericiye farklı kısa gecikmelerden sonra yeniden gönderme talimatını verir. Etkili olarak bu her iki alanı da yavaşlatır.

Yani:

1) sayfalar bugünlerde bu kadar hızlı oluşturulmaz. 2) Ethernet, çoklu eş zamanlı indirmelerle başa çıkmakta zorluk çekiyor. 3) Statik web siteleri (çok daha yaygın olarak kullanılanlar) çok daha hızlıdır ve dinamik web sitelerinden daha az paket kullanırlar.

Bu, bağlantınızı maksimize etmek gerçekten zor olduğu anlamına gelir.

1000 64Kb dosyaları koyup kodunuzun bunları ne kadar hızlı yükleyebildiğini görmek için yaptığım aynı testi deneyebilirsiniz. Benim için kodun iyi çalıştı.

+0

Simon, parti boyutunu 300 URL'ye yükselttim ve 60 saniyede aldım ... böylece daha iyiye gidiyorum: Saniyede 5 URL. Referans verdiğim literatür de ilk 64KB'yi okuyor. Nutch da çok iyi görünüyor: Saniyede 40 sayfa. Şimdi, yine de bir performans artışı olup olmadığını görmek için, dahili URL adımı 1000 olarak ayarladım. – Kiril

+0

Şimdi 1024 URL ekledim ve aynı kodla saniyede 21 sayfaya çıktım (1024'ü ~ 47 saniye içinde getiriyor). – Kiril