HttpWebRequest停止,可能只是由于页面大小

时间:2012-01-17 17:35:10

标签: c# .net httpwebrequest

我有WPF应用程序处理很多网址(数千个),每个网址发送给它自己的线程,进行一些处理并将结果存储在数据库中。

网址可以是任何东西,但有些似乎是大页面,这似乎会大大提高内存使用率并使性能非常糟糕。我在网络请求上设置了超时,因此如果花费的时间超过20秒,则不会对该网址造成影响,但似乎没有太大区别。

以下是代码部分:

               HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(urlAddress.Address);
                            req.Timeout = 20000;
                            req.ReadWriteTimeout = 20000;
                            req.Method = "GET";
                            req.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;

                            using (StreamReader reader = new StreamReader(req.GetResponse().GetResponseStream()))
                            {
                                pageSource = reader.ReadToEnd();
                                req = null;
                            }

它似乎也在读取器上停止/增加内存.ReadToEnd();

我原以为20秒的切断会有所帮助,有更好的方法吗?我假设使用asynch web方法并没有太大的优势,因为每个url下载都是自己的线程..

由于

3 个答案:

答案 0 :(得分:2)

通常,it's recommended that you use asynchronous HttpWebRequests而不是创建自己的线程。我上面链接的文章还包括一些基准测试结果。

在您阅读要结束的流后,我不知道您对页面源的操作,using string can be an issue

  

System.String类型用于任何.NET应用程序。我们有字符串   as:姓名,地址,描述,错误消息,警告甚至   应用程序设置。每个应用程序都必须创建,比较或   格式化字符串数据考虑到不变性和任何事实   对象可以转换为字符串,所有可用的内存都可以   吞噬了大量不需要的字符串重复或无人认领   字符串对象。

其他一些建议:

  • 您是否有任何防火墙限制?我看到很多问题在工作中防火墙启用速率限制并且抓取页面停止运行(一直发生在我身上)!
  • 我假设您将使用该字符串来解析HTML,因此我建议您使用Stream初始化解析器,而不是传入包含页面源的字符串(如果这是一个选项)
  • 如果您将页面源存储在数据库中,那么您无能为力。
  • 尝试通过注释来消除页面源读取作为内存/性能问题的潜在因素。
  • 使用流式HTML解析器,例如Majestic 12 - 避免将整个页面源加载到内存中(如果需要解析,再次) !
  • 限制您要下载的页面的大小,例如,仅下载150KB。 The average page size is about 100KB-130KB

此外,您能告诉我们您获取页面的初始速率是什么以及它归结为什么?您在获取页面时是否看到Web请求中的任何错误/异常?

更新

在评论部分,我注意到你创建了数千个线程,我会说你不需要这样做。从少量线程开始并不断增加它们直到您查看系统性能。一旦你开始添加线程并且性能看起来逐渐减少,那么就可以添加线程。我无法想象你需要超过128个线程(即使看起来很高)。创建固定数量的线程,例如64,让每个线程从队列中获取一个URL,获取页面,处理它,然后再返回从队列中获取页面。

答案 1 :(得分:1)

您可以使用缓冲区枚举而不是调用ReadToEnd,如果它花费的时间太长,那么您可以记录并放弃 - 例如:

static void Main(string[] args)
{
  Uri largeUri = new Uri("http://www.rfkbau.de/index.php?option=com_easybook&Itemid=22&startpage=7096");
  DateTime start = DateTime.Now;
  int timeoutSeconds = 10;
  foreach (var s in ReadLargePage(largeUri))
  {
    if ((DateTime.Now - start).TotalSeconds > timeoutSeconds)
    {
      Console.WriteLine("Stopping - this is taking too long.");
      break;
    }

  }
}

static IEnumerable<string> ReadLargePage(Uri uri)
{            
  int bufferSize = 8192;
  int readCount;
  Char[] readBuffer = new Char[bufferSize];
  HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); 
  using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
  using (StreamReader stream = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
  {
    readCount = stream.Read(readBuffer, 0, bufferSize);
    while (readCount > 0)
    {
      yield return new string(readBuffer, 0, bufferSize);
      readCount = stream.Read(readBuffer, 0, bufferSize);
    }
  }
}

答案 2 :(得分:1)

Lirik有很好的总结。

我想补充一点,如果我实现这个,我会创建一个单独的进程来读取页面。所以,这将是一个管道。第一阶段将下载URL并将其写入磁盘位置。然后将该文件排队到下一个阶段。下一阶段从磁盘读取并进行解析&amp;数据库更新。这样,您将获得下载和解析的最大吞吐量。你也可以调整你的线程池,以便你有更多的工作人员解析,等等。这个架构也非常适合分布式处理,你可以有一台机器下载,另一台主机解析/等。

另一件需要注意的事情是,如果您从多个线程击中同一台服务器(即使您使用的是Async),那么您将会遇到最大传出连接限制。您可以限制自己保持低于该值,或者增加ServicePointManager类的连接限制。