Задержка 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
, но я не вижу, как вы удаляете эти объекты. Я вижу, что вы закрываете поток ответов и ответов, но, по моему опыту, если вы не избавляетесь от всех этих объектов, вы можете получить очень длинные задержки, которые в противном случае кажутся случайными.