Недавно я попробовал запустить .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 не может ошибаться правильно?).
Любые подсказки?