Неизвестная причина истечения времени ожидания запроса HTTP HEAD

Я использую ASP.NET 3.5 для создания веб-сайта. В одной области веб-сайта отображаются 28 миниатюрных видеоизображений, которые размещены в формате jpeg на другом веб-сервере. Если один или несколько из этих jpeg-файлов не существуют, я хочу отобразить для пользователя локально размещенное изображение по умолчанию, а не разорванную ссылку на изображение в браузере.

Подход, который я выбрал для реализации этого, заключается в том, что всякий раз, когда страница отображается, она выполняет HTTP-запрос HEAD для каждого из изображений. Если я верну код состояния 200 OK, изображение будет хорошим, и я смогу выписать <img src="http://media.server.com/media/123456789.jpg" />, Если я получу 404 Not Found, то я выписываю <img src="/images/defaultthumb.jpg" />,

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

Кажется, это работает очень хорошо. Моя проблема заключается в том, что для определенных изображений запрос HTTP HEAD не выполняется с истечением времени ожидания.

Я установил очень низкое значение тайм-аута до 200 мс, чтобы не задерживать рендеринг страницы. Этот тайм-аут подходит для большинства изображений, и я попытался поэкспериментировать и увеличить его во время отладки, но это не имеет значения, даже если оно составляет 10 с или более.

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

14:24:56.799|DEBUG|[HTTP HEAD CHECK OK [http://media.server.com/adpm/505C3080-EB4F-6CAE-60F8-B97F77A43A47/videothumb.jpg]]
14:24:57.356|DEBUG|[HTTP HEAD CHECK OK [http://media.server.com/adpm/66E2C916-EEB1-21D9-E7CB-08307CEF0C10/videothumb.jpg]]
14:24:57.914|DEBUG|[HTTP HEAD CHECK OK [http://media.server.com/adpm/905C3D99-C530-46D1-6B2B-63812680A884/videothumb.jpg]]
...
14:24:58.470|DEBUG|[HTTP HEAD CHECK OK [http://media.server.com/adpm/1CE0B04D-114A-911F-3833-D9E66FDF671F/videothumb.jpg]]
14:24:59.027|DEBUG|[HTTP HEAD CHECK OK [http://media.server.com/adpm/C3D7B5D7-85F2-BF12-E32E-368C1CB45F93/videothumb.jpg]]
14:25:11.852|ERROR|[HTTP HEAD CHECK ERROR [http://media.server.com/adpm/BED71AD0-2FA5-EA54-0B03-03D139E9242E/videothumb.jpg]] The operation has timed out
Source: System
Target Site: System.Net.WebResponse GetResponse()
Stack Trace:    at System.Net.HttpWebRequest.GetResponse()
   at MyProject.ApplicationCacheManager.ImageExists(String ImageURL, Boolean UseCache) in d:\Development\MyProject\trunk\src\Web\App_Code\Common\ApplicationCacheManager.cs:line 62

14:25:12.565|ERROR|[HTTP HEAD CHECK ERROR [http://media.server.com/adpm/92399E61-81A6-E7B3-4562-21793D193528/videothumb.jpg]] The operation has timed out
Source: System
Target Site: System.Net.WebResponse GetResponse()
Stack Trace:    at System.Net.HttpWebRequest.GetResponse()
   at MyProject.ApplicationCacheManager.ImageExists(String ImageURL, Boolean UseCache) in d:\Development\MyProject\trunk\src\Web\App_Code\Common\ApplicationCacheManager.cs:line 62

14:25:13.282|ERROR|[HTTP HEAD CHECK ERROR [http://media.server.com/adpm/7728C3B6-69C8-EFAA-FC9F-DAE70E1439F9/videothumb.jpg]] The operation has timed out
Source: System
Target Site: System.Net.WebResponse GetResponse()
Stack Trace:    at System.Net.HttpWebRequest.GetResponse()
   at MyProject.ApplicationCacheManager.ImageExists(String ImageURL, Boolean UseCache) in d:\Development\MyProject\trunk\src\Web\App_Code\Common\ApplicationCacheManager.cs:line 62

Как видите, первые 25 запросов HEAD работают, а последние 3 - нет. Это всегда последние три.

Если я вставлю один из URL-адресов запроса HEAD в веб-браузер: http://media.server.com/adpm/BED71AD0-2FA5-EA54-0B03-03D139E9242E/videothumb.jpg, загружает изображение без проблем.

Чтобы попытаться выяснить, что здесь происходит, я использовал Wireshark для захвата всех HTTP-запросов, отправляемых веб-серверу, на котором размещены изображения. Для примера журнала, который я привел, я вижу 25 запросов HEAD для 25, которые были успешными, но 3, которые потерпели неудачу, НЕ появляются в следе wireshark.

Кроме изображений, имеющих различное визуальное содержание, нет разницы между изображениями.

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


Таким образом, я думаю, что из вышесказанного я могу сделать вывод, что, когда более 25 запросов HEAD происходят в быстрой последовательности, последующие запросы HEAD завершаются неудачей независимо от конкретного URL. Я также знаю, что проблема связана с сервером IIS, а не с удаленным сервером хостинга изображений, из-за отсутствия запросов в трассировке Wireshark после первых 25.

Фрагмент кода, который я использую для выполнения запросов HEAD, показан ниже. Кто-нибудь может дать мне какие-либо предложения относительно того, в чем может быть проблема? Я пробовал различные комбинации значений заголовка запроса, но, похоже, ни одно из них не имеет значения. Мне кажется, что где-то есть некоторые настройки IIS, которые ограничивают количество одновременных запросов HttpWebRequests до 25 в любом запросе страницей ASP.NET.

        try {
            HttpWebRequest hwr = (HttpWebRequest)WebRequest.Create(ImageURL);
            hwr.Method = "HEAD";
            hwr.KeepAlive = false;
            hwr.AllowAutoRedirect = false;
            hwr.Accept = "image/jpeg";
            hwr.Timeout = 200;
            hwr.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.Reload);
            //hwr.Connection = "close";

            HttpWebResponse hwr_result = (HttpWebResponse)hwr.GetResponse();
            if (hwr_result.StatusCode == HttpStatusCode.OK) {

                Diagnostics.Diags.Debug("HTTP HEAD CHECK OK [" + ImageURL + "]", HttpContext.Current.Request);

                // EXISTENCE CONFIRMED - ADD TO CACHE
                if (UseCache) {
                    _ImageExists.Value.RemoveAll(ie => ie.ImageURL == ImageURL);
                    _ImageExists.Value.Add(new ImageExistenceCheck() { ImageURL = ImageURL, Found = true, CacheExpiry = DateTime.Now.AddMinutes(5) });
                }

                // RETURN TRUE
                return true;

            } else if (hwr_result.StatusCode == HttpStatusCode.NotFound) {
                throw new WebException("404");
            } else {
                throw new WebException("ERROR");
            }            

        } catch (WebException ex) {
            if (ex.Message.Contains("404")) {

                Diagnostics.Diags.Debug("HTTP HEAD CHECK NOT FOUND [" + ImageURL + "]", HttpContext.Current.Request);

                // NON-EXISTENCE CONFIRMED - ADD TO CACHE
                if (UseCache) {
                    _ImageExists.Value.RemoveAll(ie => ie.ImageURL == ImageURL);
                    _ImageExists.Value.Add(new ImageExistenceCheck() { ImageURL = ImageURL, Found = false, CacheExpiry = DateTime.Now.AddMinutes(5) });
                }

                return false;

            } else {

                Diagnostics.Diags.Error(HttpContext.Current.Request, "HTTP HEAD CHECK ERROR [" + ImageURL + "]", ex);

                // ASSUME IMAGE IS OK
                return true;
            }

        } catch (Exception ex) {
            Diagnostics.Diags.Error(HttpContext.Current.Request, "GENERAL CHECK ERROR [" + ImageURL + "]", ex);

            // ASSUME IMAGE IS OK
            return true;
        }

1 ответ

Решение

Я решил это сам. Проблема действительно заключалась в количестве разрешенных подключений, которое по умолчанию было установлено равным 24.

В моем случае я собираюсь выполнить только проверку изображения, если MyHttpWebRequest.ServicePoint.CurrentConnections меньше 10

Чтобы увеличить максимальный предел, просто установите ServicePointManager.DefaultConnectionLimit на количество одновременных соединений вам требуется.

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

Другие вопросы по тегам