2011-05-19 80 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的计算机上,超时问题一直没有出现。