2010-12-07 224 views
9

我正在编写一个应用程序来测量我可以使用C#下载网页的速度有多快。我提供了一个唯一的域名列表,然后我产生了X个线程并执行HTTPWebRequests,直到域列表已被使用。问题是不管我使用多少个线程,我只能得到每秒大约3页。HttpWebRequest并发限制

我发现System.Net.ServicePointManager.DefaultConnectionLimit是2,但我的印象是这与每个域的连接数有关。由于列表中的每个域都是唯一的,所以这不应该成为问题。

然后我发现GetResponse()方法阻止从所有其他进程的访问,直到WebResponse被关闭:http://www.codeproject.com/KB/IP/Crawler.aspx#WebRequest,我还没有在网上找到任何其他信息来支持这个声明,但是我实现了一个HTTP请求插座,我注意到显着的加速(4倍到6倍)。

所以我的问题:有人确切知道HttpWebRequest对象是如何工作的吗?除上述内容外,还有解决方法吗?或者是否有用C#编写的高速网页爬虫的任何示例?

+0

您可以配置每个域的连接限制,但默认连接限制是全局的。 https://msdn.microsoft.com/en-us/library/fb6y0fyc.aspx – Todd 2016-12-05 01:37:45

回答

8

您是否尝试过使用异步方法,如BeginGetResponse()?

如果您使用的是.net 4.0,您可能需要尝试此代码。基本上我使用任务,以特定网站上的1000个请求(我用这个做应用程序的负载测试,我开发的机器上,我看不出有什么限制这样,因为我的应用程序是看到快速连续这些请求)

public partial class Form1 : Form 
    { 
    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     for (int i = 0; i < 1000; i++) 
     { 
     var webRequest = WebRequest.Create(textBox1.Text); 
     webRequest.GetReponseAsync().ContinueWith(t => 
     { 
      if (t.Exception == null) 
      { 
      using (var sr = new StreamReader(t.Result.GetResponseStream())) 
      { 
       string str = sr.ReadToEnd(); 
      } 
      } 
      else 
      System.Diagnostics.Debug.WriteLine(t.Exception.InnerException.Message); 
     }); 
     } 
    } 
    } 

    public static class WebRequestExtensions 
    { 
    public static Task<WebResponse> GetReponseAsync(this WebRequest request) 
    { 
     return Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null); 
    } 
    } 

由于这里的工作量是I/O限制的,产生线程来完成工作并不是必需的,事实上可能会损害性能。在WebClient类中使用异步方法使用I/O完成端口,因此性能会更高,资源更少。

3

你应该使用BeginGetResponse方法,它不会阻塞并且是异步的。

当处理I/O绑定的异步时,仅仅因为您产生了一个线程来执行I/O工作,那个线程仍然会被阻塞,等待硬件(在这种情况下是网卡)作出响应。如果您使用内置的BeginGetResponse,那么该线程将在网卡上排队,然后可用于执行更多工作。当硬件完成后,它会通知你,在这一点你的回调将被调用。

1

我想指出,BeginGetResponse方法并不完全异步的:(从MSDN

BeginGetResponse方法需要一定的同步设置任务来完成(DNS解析,代理检测和TCP套接字连接,例如)在这个方法变得异步之前。因此,不应该在用户界面(UI)线程上调用此方法,因为它可能需要一些时间,通常需要几秒钟。