2010-08-11 27 views
15

Si entiendo correctamente, un broswer almacena en caché las imágenes, los archivos JS, etc. según el nombre del archivo. Por lo tanto, existe el peligro de que si uno de esos archivos se actualiza (en el servidor), el navegador use la copia en caché.caché de archivos del lado del cliente

Una solución para este problema es cambiar el nombre de todos los archivos (como parte de la compilación), de modo que el nombre del archivo incluya un hash MD5 de sus contenidos, p.

foo.js -> foo_AS577688BC87654.js 
me.png -> me_32126A88BC3456BB.png 

Sin embargo, además de cambiar el nombre de los archivos, todas las referencias a estos archivos deben modificarse. Por ejemplo, una etiqueta como <img src="me.png"/> debe cambiarse a <img src="me_32126A88BC3456BB.png"/>.

Obviamente, esto puede ser bastante complicado, especialmente si tenemos en cuenta que las referencias a estos archivos pueden crearse dinámicamente dentro del código del lado del servidor.

Por supuesto, una solución es deshabilitar por completo el almacenamiento en caché en el navegador (y cualquier caché entre el servidor y el navegador) usando encabezados HTTP. Sin embargo, no tener almacenamiento en caché creará su propio conjunto de problemas.

¿Existe una solución mejor?

Gracias, Don

+2

¿Es esto realmente un problema? Los navegadores modernos comprobarán al menos una vez por sesión, si no todas las actualizaciones de página, para ver si una imagen o js u otro archivo referenciado se actualiza al agregar un encabezado if-modified-since en su solicitud. Si el archivo no se modifica, el servidor web devolverá 304 y el navegador usará el archivo en caché. – GrandmasterB

+0

He notado un caso en el que Safari no actualizó la caché a pesar de que el usuario usa F5. Desafortunadamente no hice una revisión en profundidad ya que fue por teléfono y solo el cliente lo estaba viendo en ese momento, así que le pedí al usuario que borrara la caché de su navegador y luego Safari compró la nueva versión. –

+0

* según el nombre del archivo * - no del todo, consulte ETags: http://en.wikipedia.org/wiki/HTTP_ETag Lo que usted haría, es usar su hash como la ETag del recurso. – Douglas

Respuesta

15

La mejor solución parece ser la versión de los nombres de archivo añadiendo la hora del último momento modificado.

Puede hacerlo de esta manera: añadir una regla de reescritura de la configuración de Apache, así:

RewriteRule ^(.+)\.(.+)\.(js|css|jpg|png|gif)$ $1.$3 

Esto redirigir cualquier URL "versionado" a la "normal". La idea es mantener sus nombres de archivo iguales, pero para beneficiarse de la memoria caché. La solución para agregar un parámetro a la URL no será óptima con algunos proxies que no almacenan en caché las URL con parámetros.

Entonces, en lugar de escribir:

<img src="image.png" /> 

Sólo tiene que llamar una función PHP:

<img src="<?php versionFile('image.png'); ?>" /> 

Con versionFile() con este aspecto:

function versionFile($file){ 
    $path = pathinfo($file); 
    $ver = '.'.filemtime($_SERVER['DOCUMENT_ROOT'].$file).'.'; 
    echo $path['dirname'].'/'.str_replace('.', $ver, $path['basename']); 
} 

Y eso es todo! El navegador pedirá image.123456789.png, Apache lo redirigirá a image.png, por lo que se beneficiará de la caché en todos los casos y no tendrá ningún problema desactualizado, sin tener que preocuparse por el control de versiones de los nombres de los archivos. .

se puede ver una explicación detallada de esta técnica aquí: http://particletree.com/notebook/automatically-version-your-css-and-javascript-files/

+0

Esta es, de lejos, la mejor solución aquí. –

+0

Gracias! :-) Pero el autor de partletree merece todo el crédito. – PJP

9

¿Por qué no añadir un número de cadena de consulta "versión" y actualizar la versión cada vez?

foo.js -> foo.js version = 5

Todavía hay un poco de trabajo durante la construcción para actualizar los números de versión, pero los nombres de archivo no es necesario cambiar.

+3

acordado como práctica común/buena. – darma

+4

Las cadenas de consulta son una mala idea si te importa el almacenamiento en caché porque la mayoría de los servidores proxy no almacenan en caché los recursos con un '?' en su URL Ver http://code.google.com/speed/page-speed/docs/caching.html. –

+2

Paul - Creo que las recomendaciones en esa página están incompletas. La sección 13.9 de RFC 2616 (la especificación de HTTP 1.1) parece implicar que las cadenas de consulta se pueden almacenar en caché si hay un encabezado expirado explícito, por lo que quizás usar una cadena de consulta sea correcto. Pero esto no significa que todas las memorias caché lo cumplan, así que probablemente me quedo modificando los nombres de archivo. –

6

Cambiar el nombre de sus recursos es el camino a seguir, aunque se utiliza un número de compilación y embed que en el nombre de archivo en lugar de un hash MD5

foo.js -> foo.123.js 

ya que significa que todos sus recursos puede cambiar el nombre de una forma determinista y resuelta en tiempo de ejecución.

A continuación, utilizamos controles personalizados para generar enlaces a los recursos en la carga de la página en función del número de compilación que se almacena en la configuración de una aplicación.

1

También he estado pensando en esto para un sitio que soporto donde sería un gran trabajo cambiar todas las referencias. Tengo dos ideas:

1. Establezca los encabezados de caducidad del caché distante y aplique los cambios que sugiere para los archivos más comúnmente descargados. Para otros archivos, configure los encabezados para que caduquen después de muy poco tiempo, por ej. 10 minutos. Luego, si tiene un tiempo de inactividad de 10 minutos al actualizar la aplicación, las memorias caché se actualizarán cuando los usuarios accedan al sitio. La navegación general del sitio debe mejorarse, ya que los archivos solo necesitarán descargarse cada 10 minutos, no cada clic.

2. Cada vez que se implementa una nueva versión de la aplicación en un contexto diferente que contiene el número de versión. p.ej. www.site.com/app_2_6_0/ No estoy muy seguro de esto ya que los marcadores de los usuarios se romperán en cada actualización.

3

Nos siguieron un patrón similar al PJP, utilizando los carriles y Nginx.

Queríamos que las imágenes de avatar del usuario se almacenaran en el caché del navegador, pero en el cambio de un avatar necesitábamos que la caché se invalidara lo antes posible.

añadimos un método para el modelo de avatar para añadir una marca de tiempo al nombre del archivo:

return "/images/#{sourcedir}/#{user.login}-#{self.updated_at.to_s(:flat_string)}.png" 

En todos los lugares en el código donde se utilizaron los avatares, que hace referencia este método en lugar de una dirección URL. En la configuración de Nginx, hemos añadido esta reescritura:

rewrite "^/images/avatars/(.+)-[\d]{12}.png" /images/avatars/$1.png; 
rewrite "^/images/small-avatars/(.+)-[\d]{12}.png"  /images/small-avatars/$1.png; 

Esto significaba que si un archivo ha cambiado, su URL en el HTML ha cambiado, por lo que el navegador del usuario realizó una nueva solicitud para el archivo. Cuando la solicitud llegó a Nginx, se reescribió con el nombre simple del archivo.

2

La mayoría de los navegadores modernos verifican el encabezado if-modified-since cuando un recurso almacenable en caché se encuentra en una solicitud HTTP. Sin embargo, no todos los navegadores admiten el encabezado if-modified-since.

Hay tres formas de "forzar" al navegador para cargar un recurso en caché.

Opción 1 Cree una cadena de consulta con una versión #. src="script.js?ver=21". La desventaja es que muchos servidores proxy no almacenan en caché un recurso con cadenas de consulta. También requiere una actualización en todo el sitio para cambios.

Opción 2 Cree un sistema de nombres para sus archivos src="script083010.js". Sin embargo, la desventaja de la opción 1 es que esto también requiere actualizaciones en todo el sitio cuando cambia un archivo.

Opción 3 Quizás la solución más elegante, simplemente configure los encabezados de almacenamiento en caché: última modificación y caduca en su servidor.La principal desventaja de esto es que los usuarios pueden tener que volver a almacenar los recursos porque caducaron pero nunca cambiaron. Además, el encabezado modificado por última vez no funciona bien cuando el contenido se sirve desde varios servidores.

Aquí unos recursos hasta la salida: YahooGoogleAskApache.com

+0

Lol lea su respuesta después de la mía, casi exactamente igual. +1 para hivemind. – Incognito

3

se recomienda usar el almacenamiento en caché por ETags en esta situación, consulte http://en.wikipedia.org/wiki/HTTP_ETag. Puede usar el hash como etag. Se enviará una solicitud para cada recurso, pero el navegador solo descargará los elementos que hayan cambiado desde la última descarga.

Lea en su servidor web/documentos de la plataforma sobre cómo usar los etags correctamente, la mayoría de las plataformas decentes tienen soporte incorporado.

2

Esto es realmente solo un problema si su servidor web establece un futuro lejano encabezado "Caduca" (estableciendo algo así como ExpiresDefault "access plus 10 years" en su configuración de Apache). De lo contrario, un navegador hará un GET condicional, basado en la hora modificada y/o el Etag. Puede verificar lo que está sucediendo en su sitio mediante un proxy web o una extensión como Firebug (en el panel Red). Su pregunta no menciona cómo está configurado su servidor web y qué encabezados está enviando con archivos estáticos.

Si no está configurando un encabezado Expires de futuro lejano, no hay nada especial que deba hacer. Su servidor web generalmente manejará GETs condicional para archivos estáticos basados ​​en la última hora modificada sin problemas. Si está configurando un encabezado Expires en el futuro lejano, entonces sí, necesita agregar algún tipo de versión al nombre del archivo como su pregunta y las otras respuestas ya han mencionado.

1

creo que una combinación de soluciones que mejor funciona:

  1. configuración de caché fechas de caducidad para cada tipo de recurso (imagen, página, etc) appropreatly para ese recurso, por ejemplo:

    • Sus páginas estáticas "Acerca de", "Contacto" etc. probablemente no vayan a cambiar más de unas pocas veces al año, por lo que fácilmente podría poner un tiempo de caché de un mes en estas páginas.
    • Las imágenes utilizadas en estas páginas podrían tener tiempos de caché eternos, ya que es más fácil reemplazar una imagen y luego cambiar una.
    • Las imágenes de avatar pueden tener un tiempo de caducidad de un día.
  2. Algunos recursos necesitan fechas modificadas en sus nombres. Por ejemplo, avatares, imágenes generadas y similares.

  3. Algunas cosas nunca deben ser cachés, páginas nuevas, contenido de usuario, etc. En estos casos, debe almacenar en caché en el servidor, pero nunca en el lado del cliente.

Al final debe tener en cuenta carfully cada tipo de recurso para determinar qué tiempo de caché para indicar al navegador para usar, y siempre sea conservitive si no está seguro. Puede aumentar el tiempo más tarde, pero es mucho más doloroso desenganchar algo.

1

Es posible que desee comprobar el enfoque adoptado por el plugin "uiperformance" de grails, que puede encontrar here. Hace muchas de las cosas que mencionas, pero las automatiza (establece el tiempo de caducidad por mucho tiempo, luego incrementa los números de versión cuando los archivos cambian).

Así que si estás utilizando grails, obtienes estas cosas de forma gratuita.Si no lo eres, tal vez puedas utilizar las técnicas empleadas.

También, tomado prestado de la página de rendimiento de ui, lea el siguiente 14 rules.

1

ETags aparentemente proporcionan una solución para esto ...

Según http://httpd.apache.org/docs/2.0/mod/core.html#fileetag, podemos configurar el navegador para generar ETags en tamaño de archivo (en lugar de tiempo/inodo/etc). Esta generación debe ser constante en múltiples implementaciones de servidor.

Simplemente activa en (/etc/apache2/apache2.conf)

FileETag Size 

& usted debe ser bueno!

De esta manera, puede simplemente hacer referencia a sus imágenes como <img src='/path/to/foo.png' /> y seguir usando todas las bondades del almacenamiento en caché de HTTP.

Cuestiones relacionadas