Задержка GetResponse HttpWebRequest на 64-битной Windows

Недавно я попробовал запустить .NET-приложение на 64-битных версиях Windows и был удивлен, заметив, что все мои вызовы HttpWebRequest.GetResponse() для веб-сервисов в моей локальной сети говорят о огромном (около 500 мс) времени для завершения. Вот несколько сведений о моей тестовой настройке:

  • .NET 3.5
  • Windows Vista Home Premium 32bit, Windows Vista Business 64bit и Windows Server 2008 64bit.

Тестовый код представляет собой слегка измененную версию примера, описанного в статье MSDN на HttpWebRequest.GetResponse. Единственные модификации, которые я выполнил, были всего лишь циклом, так что я мог бы использовать 10 последовательных вызовов и Basic Authentication, так как веб-служба была нацелена на необходимую проверку подлинности:

using System;
using System.Diagnostics;
using System.Net;
using System.Text;
using System.IO;

public class Program
{
    // Specify the URL to receive the request.
    public static void Main (string[] args)
    {
        CredentialCache crCache = null;
        Stopwatch s = new Stopwatch();
        for (int i = 0; i < 10; i++)
        {
            s.Reset();
            s.Start();
            HttpWebRequest request = 
                (HttpWebRequest)WebRequest.Create (args[0]);

            // Set some reasonable limits on resources used by this request
            request.MaximumAutomaticRedirections = 4;
            request.MaximumResponseHeadersLength = 4;
            // Set credentials to use for this request.
            if (crCache == null)
            {
                crCache = new CredentialCache();
                crCache.Add(new Uri(args[0]), "Basic",
                        new NetworkCredential("user", "password"));
            }
            request.Credentials = crCache;
            request.PreAuthenticate = true;

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            Console.WriteLine("Content length is {0}", response.ContentLength);
            Console.WriteLine("Content type is {0}", response.ContentType);

            // Get the stream associated with the response.
            Stream receiveStream = response.GetResponseStream();

            // Pipes the stream to a higher level stream reader with the required encoding format. 
            StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);

            Console.WriteLine("Response stream received.");
            //Console.WriteLine (readStream.ReadToEnd ());
            response.Close();
            readStream.Close();
            s.Stop();
            Console.WriteLine("Request took: " + s.ElapsedMilliseconds);
        }
    }
}

Я скомпилировал программный таргетинг x86 для Windows Vista Home Premium и x64 для 64-битных машин Windows. Три машины были подключены к одному сетевому коммутатору вместе с машиной, на которой размещен веб-сервис. Вот результаты, которые я получил:

  • Сборка Windows Vista Home Premium 32bit, x86. Первый вызов GetResponse() был завершен примерно в 150 мс, а все последовательные вызовы заняли около 10мс.
  • Windows Vista Business 64bit, Server 2008 64-разрядные, x86 и x64 сборки. Первый вызов занял около 1000 мс, а каждый последовательный вызов завершен в 500 мс. Если я отключу HttpWebRequest.PreAuthenticate, каждый GetResponse завершается в 1000 мс (что вполне разумно, так как в этом случае каждый запрос запускает два отдельных HTTP-запроса, один из которых заканчивается неавторизованным, а один получает правильный ответ).

Есть ли у кого-нибудь ключ к причине, по которой я получаю такие длинные задержки GetResponse в 64-битных версиях Windows?

Изменить

Дополнительная информация по проблеме:

  • Я измерил время отклика (через firebug, как было предложено), когда запрос выполняется с Firefox 3.5, и задержка запроса-ответа идентична для всех машин, то есть проблема не воспроизводится.
  • Я провел дополнительный анализ пакетов с Wireshark и придумал следующие результаты:

32bit. Разговор tcp выглядит следующим образом:

  • host- > server Новый веб-запрос HTTP GET/HTTP/1.1
  • server- > host Два сегмента TCP (~ 5 мс дельта)
  • host- > server Tcp Ack (подтверждает оба сегмента) (~ 10us delta)
  • server- > host HTTP/1.1 200 OK (~ 800us delta)
  • host- > server Новый веб-запрос HTTP GET/HTTP/1.1 и связанный TCP-код для предыдущего сегмента (HTTP/1.1 200 OK) (~ 10 мс)

64-разрядный: сеанс tcp выглядит следующим образом:

  • host- > server Новый веб-запрос HTTP GET/HTTP/1.1
  • server- > host Два сегмента TCP (~ 5 мс дельта, то же, что и 32 бит)
  • host- > server Tcp Ack (подтверждает оба сегмента) (~ 10us delta, то же, что и 32 бит)
  • server- > host HTTP/1.1 200 OK (~ 800us delta, то же, что и 32 бит)
  • host- > server TCP ack для предыдущего кадра (HTTP/1.1 200 OK) (!!! 96ms)
  • host- > server Новый веб-запрос HTTP GET/HTTP/1.1 (!!! 309ms)

500 мс, которые я получаю на 64-битных машинах, в основном происходят в последних двух шагах. Обратите внимание, что это определенно не связано со стеком TCP (так как все работает нормально с firefox). Причина, по которой мы получаем разные шаблоны TCP Ack за последние два этапа (с 32 бит, в то время как отдельный кадр TCP Ack в 64 бит), заключается в том, что новый веб-запрос задерживается на 309 + 96 мс в 64-битном случае (поэтому выходы TCP-стека отдельный кадр Ack, он не может дождаться уровня приложения).

Итак, это выглядит так:

  • Проблема вызвана дельта-временем между завершением веб-запроса и выпуском нового.
  • Проблема имеет какое-то отношение к .NET Framework? (Определенно, не связанные с TCP).
  • Проблемы возникают на складе Код Microsoft, взятый из MSDN (MS не может ошибаться правильно?).

Любые подсказки?

Ответы

Ответ 1

Просто для всех, кто сталкивается с этим потоком и имеет проблемы.

Причиной задержки в 64-битных системах является WebClient, ожидающий, пока Windows вернет значение прокси.

Напишите свой код, как это, чтобы решить эту проблему.

WebClient wc = New WebClient;
wc.Proxy = null;

Это устранит задержку, наблюдаемую некоторыми пользователями (включая меня:))

Рад, что я наконец смог вернуть что-то в сообщество, которое мне так помогло:)

Ответ 2

У меня была такая же точная проблема, и это сводило меня с ума, поскольку у меня есть 2 веб-приложения, вызывающие api webapp на одном сервере. Я продолжал получать ответы на 500 мс. Если я сделал несколько звонков на api, используя объект WebRequest, каждый из них займет около 500 мс.

Это было мое решение для моей конкретной ситуации. В моем 64-битном поле Windows Server 2008 RC2 работает Nod32. Он имеет функцию под названием "Защита веб-доступа". Отключите сканер http/https. Мое время отклика прошло от последовательных 500 мс до 30-60 мс.

Ответ 3

Можете ли вы убедиться, что

<system.net><settings><servicePointManager useNagleAlgorithm="false"/>

и повторите попытку?

Если он все еще не работает, есть ли заголовок длины содержимого? Такое же значение в обоих случаях? И, наконец, но не менее важно, есть ли лишние байты в ответе? О, и вы должны включить строку:

//Console.WriteLine (readStream.ReadToEnd ());

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

Ответ 4

Решение для меня было:

ServicePointManager.UseNagleAlgorithm = false

Обратите внимание, что вы должны установить это, прежде чем создавать объекты HttpWebRequest. Не работал у меня, если я его установлю позже.

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

Ответ 5

Также для этой ситуации может понадобиться следующий MS KB. http://support.microsoft.com/kb/823764

[ОБНОВЛЕНИЕ 9/2/10]

Разрабатывая свой комментарий выше, вы должны сделать это испытание на той же машине. Как я уже говорил, я запустил 32-битный исполняемый файл на 64-разрядной машине и посмотрю, какие измерения я получаю.

Тот факт, что приложение намного медленнее на 64-битной машине, может иметь отношение к самой конфигурации компьютера - может быть, есть багги-сетевая карта, плохой сетевой драйвер, проблемы с сетью и т.д.

Итак, сначала я сделал бы яблоки для сравнения яблок между 32-битным и 64-битным приложениями, работающими на одной и той же 64-битной машине. Это покажет, связана ли проблема с конфигурацией машины или чем-то еще.

Ответ 6

попробуйте установить keep alive в false (request.KeepAlive = false;)

Ответ 7

Как упоминалось Джеймсом Понтингом,

Причиной задержки на 64-битных системах является WebClient, ожидающий Windows, чтобы вернуть значение прокси.

Решение:

req = HttpWebRequest.Create(url) as HttpWebRequest;
req.Proxy = null;
res = req.GetResponse() as HttpWebResponse;

Ответ 8

HttpWebRequest и HttpWebResponse оба реализуют IDisposable, но я не вижу, как вы удаляете эти объекты. Я вижу, что вы закрываете поток ответов и ответов, но, по моему опыту, если вы не избавляетесь от всех этих объектов, вы можете получить очень длинные задержки, которые в противном случае кажутся случайными.