Ограничение параллелизма для HttpWebRequest

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

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

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

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


person Kam Sheffield    schedule 07.12.2010    source источник
comment
Вы можете настроить лимит подключений для каждого домена, но по умолчанию лимит подключений является глобальным. msdn.microsoft.com/en-us/library/fb6y0fyc.aspx   -  person Todd    schedule 05.12.2016


Ответы (3)


Пробовали ли вы использовать асинхронные методы, такие как BeginGetResponse()?

Если вы используете .net 4.0, вы можете попробовать этот код. По сути, я использую Tasks для выполнения 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);
    }
  }

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

person Shiv Kumar    schedule 07.12.2010

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

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

person BFree    schedule 07.12.2010

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

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

person apdevelop    schedule 07.05.2012