Подтвердить что ты не робот

Настройка времени ожидания подключения HttpWebRequest в С#

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

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

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?"

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

I Set:

.Timeout=5000
.ReadWriteTimeout=32000

Время соединения и времени для HttpWebRequest заняло 26 мс

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

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

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

Ответ 3

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

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

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

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

Edit:

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

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

(Подчеркните мой.)

Ответ 4

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

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

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

Ответ 5

Независимо от того, что мы пробовали, нам не удалось получить тайм-аут ниже 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;
}