Я использую 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;
}