2011-05-19 81 views
5

我正在開發一個應用程序,我需要下載一堆網頁,最好儘可能快。我現在這樣做的方式是我有多個線程(100),它們各自擁有System.Net.HttpWebRequest。這類作品,但我沒有得到我想要的表演。目前,我有一個600+ Mb/s的強大連接,而且這個連接最多隻能使用10%(峯值時)。我想我的策略是有缺陷的,但我無法找到任何其他好的方法來做到這一點。優化多個網頁的下載。 C#

另外:如果使用HttpWebRequest不是一個好的下載網頁的方式,請這麼說:) 該代碼已經從java半自動轉換。

謝謝:)

更新:

public String getPage(String link){ 
    myURL = new System.Uri(link); 
    myHttpConn = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(myURL); 
    myStreamReader = new System.IO.StreamReader(new System.IO.StreamReader(myHttpConn.GetResponse().GetResponseStream(), 
      System.Text.Encoding.Default).BaseStream, 
       new System.IO.StreamReader(myHttpConn.GetResponse().GetResponseStream(), 
        System.Text.Encoding.Default).CurrentEncoding); 

     System.Text.StringBuilder buffer = new System.Text.StringBuilder(); 

     //myLineBuff is a String 
     while ((myLineBuff = myStreamReader.ReadLine()) != null) 
     { 
      buffer.Append(myLineBuff); 
     } 
    return buffer.toString(); 
} 
+0

給我們一個你目前的戰略描述。或許有代碼;) – Stormenet 2011-05-19 16:55:43

+7

數以百計的線程很少很好 – Dyppl 2011-05-19 16:56:24

+2

使用100個線程可能無濟於事,因爲我從未聽說過有多少個邏輯內核。您應該創建一些等於PC上邏輯核心數量的線程,並提高它們的優先級。另外,我想知道爲每個人製作新的System.Net.HttpWebRequest需要多少開銷?這些不能以某種方式重用嗎?你如何存儲這些頁面? – MAW74656 2011-05-19 16:59:20

回答

2

的一個問題是,它似乎你發出的每個請求兩次:

myStreamReader = new System.IO.StreamReader(
    new System.IO.StreamReader(
     myHttpConn.GetResponse().GetResponseStream(), 
     System.Text.Encoding.Default).BaseStream, 
      new System.IO.StreamReader(myHttpConn.GetResponse().GetResponseStream(), 
       System.Text.Encoding.Default).CurrentEncoding); 

這使得兩次調用GetResponse。由於我無法理解的原因,您還創建了兩個流讀取器。你可以分開它並簡化它,並且還可以更好地處理錯誤...

var response = (HttpWebResponse)myHttpCon.GetResponse(); 
myStreamReader = new StreamReader(response.GetResponseStream(), Encoding.Default) 

這應該是你的有效吞吐量的兩倍。

此外,你可能想要確保處理你正在使用的對象。當你下載了很多頁面時,如果你沒有自行清理,你可能會很快耗盡資源。在這種情況下,您應該致電response.Close()。見http://msdn.microsoft.com/en-us/library/system.net.httpwebresponse.close.aspx

+0

是的!謝謝!這改善了我的代碼!這不是一個2倍的改進,但它是+ 50%的提升!非常感謝:) 順便說一句,我使用它後關閉連接:)(忘了把它放在這裏) – Automatico 2011-05-28 22:33:18

1

我這樣做同樣的事情,但成千上萬的提供XML和文本內容的傳感器。絕對會影響性能的因素不僅限於帶寬和計算機的速度和功率,還包括您所聯繫的每臺服務器的帶寬和響應時間,超時延遲,每次下載的大小以及每臺服務器的可靠性遠程互聯網連接。

正如註釋所示,數百個線程不一定是個好主意。目前我發現一次運行20到50個線程似乎是最佳的。在我的技術中,每個線程完成一次下載,就會從隊列中獲得下一個項目。

我在一個單獨的線程上運行自定義的ThreaderEngine類,該線程負責維護工作項的隊列並根據需要分配線程。本質上它是一個循環遍歷一組線程。隨着線程完成,它從隊列中抓取下一個項目並再次啓動線程。

我的線程中的每個實際上正在下載幾個單獨的項目,但該方法調用相同(.NET 4.0)

public static string FileDownload(string _ip, int _port, string _file, int Timeout, int ReadWriteTimeout, NetworkCredential _cred = null) 
{ 
    string uri = String.Format("http://{0}:{1}/{2}", _ip, _port, _file); 
    string Data = String.Empty; 
    try 
    { 
     HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(uri); 
     if (_cred != null) Request.Credentials = _cred; 
     Request.Timeout = Timeout; // applies to .GetResponse() 
     Request.ReadWriteTimeout = ReadWriteTimeout; // applies to .GetResponseStream() 
     Request.Proxy = null; 
     Request.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore); 
     using (HttpWebResponse Response = (HttpWebResponse)Request.GetResponse()) 
     { 
      using (Stream dataStream = Response.GetResponseStream()) 
      { 
       if (dataStream != null) 
        using (BufferedStream buffer = new BufferedStream(dataStream)) 
        using (StreamReader reader = new StreamReader(buffer)) 
        { 
         Data = reader.ReadToEnd(); 
        } 
      } 
      return Data; 
     } 
    } 
    catch (AccessViolationException ave) 
    { 
     // ... 
    } 
    catch (Exception exc) 
    { 
     // ... 
    } 
} 

使用這個我能夠從1200+下載約60KB每遠程機器(72MB)不到5分鐘。該機器是具有2GB RAM的Core 2 Quad,並使用四個綁定的T1連接(〜6Mbps)。

+0

那麼,這是一個非常好的描述我的工作:p 我也使用另一個線程來分配工作和東西,但我真的對下載的性能感到失望。目前,我正在獲得約30頁/秒。與我上網的速度相比,這個數字應該可能高出很多,甚至可能高出10倍。 我幾乎不使用任何cpu(6核機器的27%峯值)。 – Automatico 2011-05-19 17:32:07

+0

@ Cort3z看到我對你的問題的評論 - 我想知道遠程服務器是否限制了你可以建立的同時連接的數量。 – JYelton 2011-05-19 17:32:48

2

我加入這個答案作爲另一種可能性,當使用使用Windows XP或Vista作爲操作系統 多線程應用程序

  • 從多個服務器下載

    • 人可能會遇到

      這些操作系統的tcpip.sys驅動程序每秒有10個出站連接的限制。這是一個速率限制,而不是連接限制,所以您可以擁有數百個連接,但不能啓動超過10個/秒。微軟強制限制某些類型病毒/蠕蟲的傳播。這種方法是否有效不在此答案的範圍內。

      在從多個服務器下載的多線程應用程序中,此限制可能表現爲一系列超時。一旦達到10/s限制,Windows將所有「半開」(新開但尚未建立)連接放入隊列。例如,在我的應用程序中,我有20個線程可以處理連接,但是我發現有時候我會從我知道正在運行和可以訪問的服務器中獲取超時。

      要驗證是否發生這種情況,請檢查System下的操作系統的事件日誌。錯誤是:

      EventID 4226: TCP/IP has reached the security limit imposed on the number of concurrent TCP connect attempts.

      有以補丁的這個錯誤和大量多次提到並修復適用於取消該限制。但是,由於P2P(Torrent)用戶經常遇到這個問題,因此該補丁存在相當多的惡意軟件僞裝。

      我有要求以5分鐘爲間隔從1200多臺服務器(實際上是數據傳感器)收集數據。我最初開發的應用程序(在WinXP上)重複使用20個線程來爬取服務器列表並將數據聚合到SQL數據庫中。因爲連接是基於計時器滴答事件啓動的,所以這種錯誤經常發生,因爲在他們的調用時,沒有建立連接,因此10個連接立即排隊。

      請注意,這不是一個問題必然,因爲隨着連接建立,那些排隊然後處理。但是,如果非排隊連接建立速度很慢,那麼可能會對排隊連接的超時限制產生負面影響(以我的經驗)。結果,看着我的應用程序日誌文件,我會看到一批超時的連接,其次是大部分成功的連接。打開網絡瀏覽器來測試「超時」連接是令人困惑的,因爲服務器可用且快速響應。

      我決定嘗試使用HEX編輯tcpip.sys文件,這是在a guide at speedguide.net上建議的。我的文件的校驗和與指南不同(我的SP3不是SP2),指南中的註釋並不一定有幫助。但是,我確實發現了a patch that worked for SP3,並且在應用它後發現了一個直接的差異。

      從我所能找到的信息來看,Windows 7沒有這個限制,並且由於將應用程序移動到基於Windows 7的計算機上,超時問題一直沒有出現。