Ограничение параллелизма на HttpWebRequest - PullRequest
9 голосов
/ 08 декабря 2010

Я пишу приложение, чтобы измерить, как быстро я могу загружать веб-страницы с помощью C #.Я предоставляю список уникальных доменных имен, затем создаю X потоков и выполняю HTTPWebRequests до тех пор, пока список доменов не будет использован.Проблема в том, что независимо от того, сколько потоков я использую, я получаю только около 3 страниц в секунду.

Я обнаружил, что System.Net.ServicePointManager.DefaultConnectionLimit равен 2, но у меня сложилось впечатление, что это связано с количеством соединений на домен.Поскольку каждый домен в списке уникален, это не должно быть проблемой.

Затем я обнаружил, что метод GetResponse () блокирует доступ из всех других процессов до тех пор, пока WebResponse не будет закрыт: http://www.codeproject.com/KB/IP/Crawler.aspx#WebRequest, Я не нашел никакой другой информации в Интернете, чтобы поддержать это утверждение, однакоЯ реализовал HTTP-запрос с использованием сокетов и заметил значительное ускорение (от 4 до 6 раз).

Итак, мои вопросы: кто-нибудь точно знает, как работают объекты HttpWebRequest? Есть ли обходной путь, помимо того, что было упомянуто?выше? или есть какие-нибудь примеры высокоскоростных веб-сканеров, написанных где-нибудь на C #?

Ответы [ 3 ]

8 голосов
/ 08 декабря 2010

Вы пытались использовать асинхронные методы, такие как 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);
    }
  }

Поскольку рабочая нагрузка здесь ограничена вводом / выводом, создание потоков для выполнения работы не требуется и фактически может снизить производительность. При использовании методов Async в классе WebClient используются порты завершения ввода-вывода, поэтому они будут намного более производительными и менее ресурсоемкими.

3 голосов
/ 08 декабря 2010

Вы должны использовать метод BeginGetResponse , который не блокирует и является асинхронным.

При работе с асинхронностью, связанной с вводом / выводом, только потому, что вы порождаете поток для выполнения I/ O работает, этот поток все еще будет заблокирован в ожидании ответа оборудования (в данном случае сетевой карты).Если вы используете встроенный BeginGetResponse, то этот поток просто поставит его в очередь на сетевой карте и станет доступен для дополнительной работы.Когда оборудование будет готово, оно уведомит вас, после чего будет вызван ваш обратный вызов.

1 голос
/ 07 мая 2012

Хотелось бы отметить, что BeginGetResponse метод не является полностью асинхронным: (из MSDN )

BeginGetResponse Для этого метода требуется выполнить некоторые задачи синхронной настройки (например, разрешение DNS, обнаружение прокси-сервера и подключение к сокету TCP), прежде чем этот метод станет асинхронным.В результате этот метод никогда не должен вызываться в потоке пользовательского интерфейса (UI), поскольку это может занять некоторое время, обычно несколько секунд.

...