HttpWebRequest Приостановка, возможно только из-за размера страницы - PullRequest
3 голосов
/ 17 января 2012

У меня есть приложение WPF, которое обрабатывает много URL-адресов (тысяч), каждый из которых отправляет в свой собственный поток, выполняет некоторую обработку и сохраняет результат в базе данных.

URL-адреса могут быть любыми, но некоторые из них выглядят большими по размеру страницами, что, похоже, значительно увеличивает использование памяти и ухудшает производительность. Я установил тайм-аут для веб-запроса, поэтому, если он занял больше 20 секунд, он не беспокоится об этом URL, но, похоже, не имеет большого значения.

Вот код раздела:

               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 секунд поможет, есть ли лучший метод? Я предполагаю, что использование асинхронного веб-метода не дает больших преимуществ, так как каждая загрузка URL в любом случае происходит в отдельном потоке.

Спасибо

Ответы [ 3 ]

2 голосов
/ 17 января 2012

Как правило, рекомендуется использовать асинхронный HttpWebRequests вместо создания собственных потоков.В статье, на которую я ссылался выше, также есть некоторые результаты бенчмаркинга.

Я не знаю, что вы делаете с источником страницы после прочтения потока до конца, но использование строки может бытьПроблема :

Тип System.String используется в любом приложении .NET.У нас есть строки: имена, адреса, описания, сообщения об ошибках, предупреждения или даже настройки приложения.Каждое приложение должно создавать, сравнивать или форматировать строковые данные.Учитывая неизменность и тот факт, что любой объект может быть преобразован в строку, вся доступная память может быть поглощена огромным количеством нежелательных дубликатов строк или невостребованных строковых объектов.

Некоторые другие предложения:

  • Есть ли у вас какие-либо ограничения брандмауэра? Я видел много проблем в работе, когда брандмауэр разрешает ограничение скорости и выбор страниц останавливается (случается со мной всевремя)!
  • Я предполагаю, что вы собираетесь использовать строку для разбора HTML, поэтому я бы порекомендовал вам инициализировать ваш анализатор с Stream вместо передачи строки, содержащей исходный код страницы (если это вариант).
  • Если вы храните источник страницы в базе данных, вы ничего не можете сделать.
  • Попробуйте исключить чтение источника страницы какпотенциальный вкладчик в проблему с памятью и производительностью, закомментировав ее.
  • Используйте анализатор потокового HTML , например Majestic 12 - избавляет от необходимости загружать весь источник страницы в память (опять же, , если вам нужно проанализировать )!
  • Ограничить размер страниц, которые вы собираетесьскачать, скажем, только скачать 150KB. Средний размер страницы составляет около 100 КБ-130 КБ

Кроме того, можете ли вы сказать нам, какова ваша первоначальная скорость загрузки страниц и к чему она сводится?Видите ли вы какие-либо ошибки / исключения из веб-запроса при загрузке страниц?

Обновление

В разделе комментариев я заметил, что вы создаете тысячи потоков, и я бы сказал, чтотебе не нужно этого делать.Начните с небольшого количества потоков и продолжайте увеличивать их, пока не увеличите производительность своей системы.Как только вы начинаете добавлять потоки, и производительность выглядит так, как будто она снижается, добавьте потоки.Я не могу представить, что вам понадобится более 128 потоков (даже если это кажется высоким).Создайте фиксированное количество потоков, например 64, пусть каждый поток берет URL из вашей очереди, извлекает страницу, обрабатывает ее и затем снова возвращается к получению страниц из очереди.

1 голос
/ 18 января 2012

У Лирика действительно хорошее резюме.

Я бы добавил, что если бы я это реализовывал, я бы сделал отдельный процесс, который читает страницы. Так что это будет трубопровод. Первый этап - загрузить URL-адрес и записать его на диск. А затем поставьте этот файл в очередь на следующий этап. Следующий этап читает с диска и выполняет синтаксический анализ и обновление БД. Таким образом, вы получите максимальную пропускную способность при загрузке и разборе. Вы также можете настроить свои пулы потоков так, чтобы у вас было больше рабочих, разбирающих и т. Д. Эта архитектура также очень хорошо подходит для распределенной обработки, где вы можете загружать один компьютер и другой хост, и т. Д.

Еще одна вещь, на которую следует обратить внимание: если вы подключаетесь к одному и тому же серверу из нескольких потоков (даже если вы используете Async), то вы столкнетесь с максимальным пределом исходящего соединения. Вы можете ограничить себя, чтобы остаться ниже этого уровня, или увеличить лимит подключения в классе ServicePointManager.

1 голос
/ 17 января 2012

Вы можете перечислить с буфером вместо вызова 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);
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...