Aproveche el almacenamiento en caché del navegador en IIS (problema de velocidad de la página de google)

Hay varias preguntas sobre cómo aprovechar el almacenamiento en caché del navegador, pero no encontré nada útil sobre cómo hacerlo en una aplicación ASP.NET. La velocidad de páginas de Google dice que este es el mayor problema del rendimiento. Hasta ahora lo hice en mi web.config :

  <!-- -->    

El código comentado funciona. Puedo establecer que el encabezado expire sea un tiempo particular en el futuro, pero no pude establecer cacheControlMaxAge para establecer cuántos días a partir de ahora el contenido estático se almacenaría en caché. No funciona. Mi pregunta es:

¿Cómo puedo hacer eso? Sé que es posible configurar el almacenamiento en caché solo para una carpeta específica, lo que sería una buena solución, pero tampoco funciona. La aplicación está alojada en Windows Server 2012, en IIS8, el grupo de aplicaciones está configurado en clásico.

Después de configurar este código en la configuración web, obtuve una velocidad de 72 páginas (antes era 71). 50 archivos no fueron almacenados en caché. (Ahora 49) Me preguntaba por qué y me acabo de dar cuenta de que un archivo en realidad estaba en la memoria caché (archivo svg). Lamentablemente, los archivos png y jpg no lo eran. Esta es mi web.config

    
"

EDITAR: si configuro la fecha de caducidad exacta, el almacenamiento en caché está funcionando, pero no para jpg, gif …. solo para png

EDIT2: Si configuro cacheControlCustom="public" como aquí:

  

el almacenamiento en caché está funcionando, pero aún no es para jpegs y gifs; solo funciona para svgs y pngs.

La mayoría de los problemas de caché del navegador se pueden resolver al ver los encabezados de respuesta (se puede hacer en las herramientas de desarrollo chrome de Google).

enter image description here

Ahora, la sección de clientCache de su archivo web.config debe establecer el almacenamiento en caché de salida a una edad máxima, como puede ver en la imagen que se muestra a continuación. Estableció la max-age en 86400 que es 1 día en segundos.

Aquí está el fragmento web.config para esta configuración.

  

Ahora que es genial, el encabezado de respuesta tiene una propiedad de max-age configurada en el encabezado Cache-Control . Entonces, el navegador debe almacenar en caché el contenido. Bueno, esto es cierto en su mayoría, pero algunos navegadores requieren que se establezca otra bandera. Específicamente, el indicador public establecido para el encabezado de control de caché. Esto se puede agregar fácilmente mediante el uso del atributo cacheControlCustom en web.config . Aquí hay un ejemplo.

  

Ahora cuando volvemos a intentar la página e inspeccionamos los encabezados.

enter image description here

Ahora, como puede ver en la imagen de arriba, ahora tenemos el valor public, max-age=86400 . Por lo tanto, nuestro navegador tiene todo lo que necesita para almacenar en caché los recursos. Ahora, examinar los encabezados y la pestaña de red de google chrome nos ayudará.

Aquí está la primera solicitud al archivo. Tenga en cuenta que el archivo no está en la memoria caché … enter image description here

Ahora naveguemos de regreso a esta página ( NOTA: no actualice la página, hablaremos de eso en un segundo). Verás que la respuesta ahora regresa de la memoria caché (como un círculo).

enter image description here

Ahora qué sucede si actualizo la página usando F5 o usando la característica de actualización del navegador. Espera … ¿a dónde fue el (from cache) ? enter image description here

Bueno, en Google Chrome (no estoy seguro acerca de otros navegadores), al usar el botón Actualizar volverá a descargar los recursos estáticos independientemente del encabezado del caché ( inserte la aclaración aquí, por favor ). Eso significa que los recursos se han recuperado y enviado el encabezado de edad máxima.

Ahora, después de toda la explicación anterior, asegúrese de probar cómo está supervisando los encabezados de la caché.

Actualizar

Según sus comentarios, indicó que tiene un controlador genérico ( IHttpHandler ) llamado Image.ashx con el tipo de contenido de image/jpg . Ahora puede esperar que el comportamiento predeterminado sea almacenar en caché este controlador. Sin embargo, IIS ve la extensión .ashx (correctamente) como una secuencia de comandos dinámica y no está sujeta al almacenamiento en caché sin establecer explícitamente los encabezados de la memoria caché en el código mismo.

Ahora, aquí es donde debe tener cuidado, ya que, en general, IHttpHandlers no debe almacenarse en caché, ya que suelen ofrecer contenido dynamic. Ahora, si es poco probable que ese contenido cambie, puede establecer sus encabezados de caché directamente en el código. Aquí hay un ejemplo de establecer encabezados de caché en IHttpHandlers usando el contexto de Response .

 context.Response.ContentType = "image/jpg"; context.Response.Cache.SetMaxAge(TimeSpan.FromDays(1)); context.Response.Cache.SetCacheability(HttpCacheability.Public); context.Response.Cache.SetSlidingExpiration(true); context.Response.TransmitFile(context.Server.MapPath("~/out.jpg")); 

Ahora mirando el código estamos configurando algunas propiedades en la propiedad Cache . Para obtener la respuesta deseada, he establecido las propiedades.

  • context.Response.Cache.SetMaxAge(TimeSpan.FromDays(1)); le dice a la memoria caché de salida que establezca que la max-age= parte del encabezado de Cache-Control sea ​​de 1 día en el futuro (86400 segundos).
  • context.Response.Cache.SetCacheability(HttpCacheability.Public); le dice al caché de salida que establezca el encabezado Cache-Control en public . Esto es bastante importante ya que le dice al navegador que guarde en caché para objetar.
  • context.Response.Cache.SetSlidingExpiration(true); le dice a la memoria caché de salida que se asegure de que está configurando la max-age= parte del encabezado de Cache-Control correctamente. Sin configurar el caducidad de deslizamiento, el caché de salida de IIS ignorará el encabezado de antigüedad máxima. Juntar esto me da este resultado.

Caché de salida del archivo ashx

Como .ashx anteriormente, es posible que no desee almacenar en caché los archivos .ashx , ya que normalmente entregan contenido dynamic. Sin embargo, si no es probable que ese contenido dynamic cambie en un período determinado, puede usar los métodos anteriores para entregar su archivo .ashx .

Ahora, junto con el proceso mencionado anteriormente, también puede configurar el componente ETag (ver wiki) de los encabezados de la memoria caché para que el navegador pueda verificar el contenido entregado por una cadena personalizada. La wiki dice:

Un ETag es un identificador opaco asignado por un servidor web a una versión específica de un recurso que se encuentra en una URL. Si alguna vez cambia el contenido del recurso en esa URL, se asigna una ETag nueva y diferente.

Entonces, este es realmente un tipo de identificación única para que el navegador identifique el contenido que se está entregando en la respuesta. Al proporcionar este encabezado, el navegador en la próxima recarga enviará un encabezado If-None-Match con el ETag de la última respuesta. Podemos modificar nuestro controlador para detectar el encabezado If-None-Match y compararlo con nuestro propio Etag generado. Ahora no existe una ciencia exacta para generar ETags pero una buena regla de oro es entregar un identificador que muy probablemente definirá solo una entidad. En este caso, me gusta usar dos cadenas concatenadas juntas, como.

 System.IO.FileInfo file = new System.IO.FileInfo(context.Server.MapPath("~/saveNew.png")); string eTag = file.Name.GetHashCode().ToString() + file.LastWriteTimeUtc.Ticks.GetHashCode().ToString(); 

En el fragmento anterior, estamos cargando un archivo de nuestro sistema de archivos (puede obtenerlo desde cualquier lugar). Luego estoy usando el método GetHashCode() (en todos los objetos) para obtener el código hash entero del objeto. En el ejemplo, selecciono el hash del nombre del archivo, luego la última fecha de escritura. El motivo de la última fecha de escritura es que, en caso de que se cambie el archivo, también se modifique el código hash, lo que hace que las huellas dactilares sean diferentes.

Esto generará un código hash similar a 306894467-210133036 .

Entonces, ¿cómo usamos esto en nuestro controlador? A continuación se muestra la versión recientemente modificada del controlador.

 System.IO.FileInfo file = new System.IO.FileInfo(context.Server.MapPath("~/out.png")); string eTag = file.Name.GetHashCode().ToString() + file.LastWriteTimeUtc.Ticks.GetHashCode().ToString(); var browserETag = context.Request.Headers["If-None-Match"]; context.Response.ClearHeaders(); if(browserETag == eTag) { context.Response.Status = "304 Not Modified"; context.Response.End(); return; } context.Response.ContentType = "image/jpg"; context.Response.Cache.SetMaxAge(TimeSpan.FromDays(1)); context.Response.Cache.SetCacheability(HttpCacheability.Public); context.Response.Cache.SetSlidingExpiration(true); context.Response.Cache.SetETag(eTag); context.Response.TransmitFile(file.FullName); 

Como puede ver, he cambiado bastante el controlador, sin embargo, notará que generamos el hash Etag , verifique si hay un encabezado If-None-Match entrante. Si el hash etag y el encabezado son iguales, le diremos al navegador que el contenido no ha cambiado al devolver el código de estado 304 Not Modified .

El siguiente fue el mismo controlador, excepto que agregamos el encabezado ETag llamando:

 context.Response.Cache.SetETag(eTag); 

Cuando ejecutamos esto en el navegador obtenemos.

Control de caché con ETag

Verá en la imagen (como cambié el nombre del archivo) que ahora tenemos todos los componentes de nuestro sistema de caché en su lugar. El ETag se está entregando como un encabezado, y el navegador está enviando el encabezado de solicitud If-None-Match para que nuestro manejador pueda responder en consecuencia al cambio del archivo de caché.

Utilizar esta. Esto es trabajo para mi

    
         

Usando lo anterior, los archivos de contenido estático se almacenarán en caché durante 10 días en el navegador. Puede encontrar información detallada sobre el elemento aquí .

También puede usar el elemento para definir la configuración de caché para un archivo específico: