Настройка времени ожидания соединения HttpWebRequest в C # - PullRequest
43 голосов
/ 01 октября 2009

Я полагаю, что после долгих исследований и поисков я обнаружил, что то, что я хочу сделать, вероятно, лучше обслуживать, устанавливая асинхронное соединение и прерывая его после желаемого времени ожидания ... Но я пойду дальше и спрошу в любом случае!

Быстрый фрагмент кода:

HttpWebRequest webReq = (HttpWebRequest)HttpWebRequest.Create(url);
webReq.Timeout = 5000;
HttpWebResponse response = (HttpWebResponse)webReq.GetResponse(); 
// this takes ~20+ sec on servers that aren't on the proper port, etc.

У меня есть метод HttpWebRequest, который используется в многопоточном приложении, в котором я подключаюсь к большому количеству веб-серверов компании. В случаях, когда сервер не отвечает, время ожидания HttpWebRequest.GetResponse() составляет около 20 секунд, хотя я указал время ожидания всего 5 секунд. В целях обеспечения регулярного доступа к серверам я хочу пропустить те, для подключения к которым требуется больше 5 секунд.

Итак, вопрос таков: «Существует ли простой способ указать / уменьшить время ожидания соединения для WebRequest или HttpWebRequest?»

Ответы [ 5 ]

57 голосов
/ 01 октября 2009

Я считаю , что проблема в том, что WebRequest измеряет время только после того, как запрос действительно сделан. Если вы отправляете несколько запросов на один и тот же адрес, то ServicePointManager будет блокировать ваши запросы и только на самом деле отправит столько одновременных соединений, сколько указано в соответствующем ServicePoint.ConnectionLimit, которое по умолчанию получает значение из ServicePointManager.DefaultConnectionLimit. Хост приложения CLR устанавливает значение 2, хост ASP - 10. Таким образом, если у вас есть многопоточное приложение, которое отправляет несколько запросов одному и тому же хосту, только два фактически помещаются в провод, остальные помещаются в очередь.

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

Другим фактором, который следует учитывать, является время поиска DNS. Опять же, мое убеждение не подкреплено убедительными доказательствами, но я думаю, что WebRequest не не считает время поиска DNS и время ожидания запроса. Время поиска DNS может показаться очень большим фактором времени в некоторых развертываниях.

И да, вы должны закодировать свое приложение в виде WebRequest.BeginGetRequestStream (для POST s с контентом) и WebRequest.BeginGetResponse (для GET s и POSTS с). Синхронные вызовы не будут масштабироваться (я не буду вдаваться в детали, почему, но у меня есть do веские доказательства). В любом случае, проблема ServicePoint ортогональна этому: поведение в очереди также происходит с асинхронными вызовами.

33 голосов
/ 21 сентября 2010

Извините за привязку к старому потоку, но я думаю, что то, что было сказано выше, может быть неправильным / вводящим в заблуждение.

Из того, что я могу сказать. Время ожидания - это НЕ время соединения, это ОБЩЕЕ время, отведенное для всей жизни запроса HttpWebRequest и ответа. Доказательство:

Я установил:

.Timeout=5000
.ReadWriteTimeout=32000

Время подключения и отправки для HttpWebRequest заняло 26 мс

но время последующего вызова HttpWebRequest.GetResponse () истекло в 4974 мс, что доказывает, что 5000 мс был пределом времени для всего набора запросов на отправку / получение ответа.

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

Например: я готов подождать 30 секунд с запросом на соединение, который может вернуть результат, но я хочу сжечь только 10 секунд, ожидая отправки запроса на хост, который работает некорректно.

19 голосов
/ 11 декабря 2009

Что-то, что я нашел позже, помогло, это свойство .ReadWriteTimeout. Это, в дополнение к свойству .Timeout, похоже, в конечном итоге сократило время, которое потоки потратили бы на попытку загрузки с проблемного сервера. Время по умолчанию для .ReadWriteTimeout составляет 5 минут, что для моего приложения было слишком длинным.

Итак, мне кажется:

.Timeout = время, потраченное на попытку установить соединение (не включая время поиска) .ReadWriteTimeout = время, потраченное на чтение или запись данных после установления соединения

Дополнительная информация: Свойство HttpWebRequest.ReadWriteTimeout

Edit:

Согласно комментарию @ KyleM, свойство Timeout предназначено для всей попытки подключения, и чтение его в MSDN показывает:

Тайм-аут - это количество миллисекунд, в течение которых последующий синхронный запрос, выполненный с помощью метода GetResponse, ожидает ответа, а метод GetRequestStream ожидает потока. Время ожидания применяется ко всему запросу и ответу, а не индивидуально к вызовам методов GetRequestStream и GetResponse. Если ресурс не возвращается в течение периода ожидания, запрос вызывает WebException со свойством Status, для которого установлено значение WebExceptionStatus.Timeout.

(Акцент мой.)

14 голосов
/ 28 января 2010

Из документации свойства HttpWebRequest.Timeout:

Запрос системы доменных имен (DNS) может возврат займет до 15 секунд или время вышло Если ваш запрос содержит имя хоста, которое требует разрешения и Вы устанавливаете Тайм-аут на значение меньше 15 секунд, это может занять 15 секунд или больше, прежде чем выбрасывается WebException указать время ожидания по вашему запросу.

Возможно ли, что ваш DNS-запрос является причиной тайм-аута?

7 голосов
/ 26 сентября 2014

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

Чтобы обойти эту проблему, мы объединили проверку TcpClient, чтобы проверить, был ли домен активен, после чего последовала отдельная проверка, чтобы проверить, был ли URL активен

public static bool IsUrlAlive(string aUrl, int aTimeoutSeconds)
{
    try
    {
        //check the domain first
        if (IsDomainAlive(new Uri(aUrl).Host, aTimeoutSeconds))
        {
            //only now check the url itself
            var request = System.Net.WebRequest.Create(aUrl);
            request.Method = "HEAD";
            request.Timeout = aTimeoutSeconds * 1000;
            var response = (HttpWebResponse)request.GetResponse();
            return response.StatusCode == HttpStatusCode.OK;
        }
    }
    catch
    {
    }
    return false;

}

private static bool IsDomainAlive(string aDomain, int aTimeoutSeconds)
{
    try
    {
        using (TcpClient client = new TcpClient())
        {
            var result = client.BeginConnect(aDomain, 80, null, null);

            var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(aTimeoutSeconds));

            if (!success)
            {
                return false;
            }

            // we have connected
            client.EndConnect(result);
            return true;
        }
    }
    catch
    {
    }
    return false;
}
...