Últimos Cambios |
||
Blog personal: El hilo del laberinto |
Última Actualización: 18 de Abril de 2.000 - Martes
Prácticamente todos los navegadores del mercado van "capturando" en memoria y en disco las páginas HTML, elementos gráficos, etc., que van descargando de la red a medida que el usuario va recorriendo enlaces. Ello es bueno, ya que si el usuario regresa a la página en otro momento encontrará que, por un lado, se carga muy rápido y, por otro, el proveedor de contenidos se verá menos saturado.
En resumen, las ventajas de la tecnología caché son, fundamentalmente:
Como no todo pueden ser ventajas, el empleo de cachés tiene también algunos problemas:
En este documento vamos a desentrañar algunos de los secretos y trucos necesarios para procurar, en lo posible, que un sitio web sea lo más cache friendly posible.
El protocolo que interconecta los clientes y los servidores web se llama HTTP (HyperText Transfer Protocol), y su entendimiento es crucial para poder entender los mecanismos "caché".
Para poder seguir el resto del documento se requieren unas nociones y unas herramientas mínimas. Los lectores famirializados con el idioma Inglés deberían curiosear la especificación HTTP, cuya URL aparece al final de este documento.
Veamos, por ejemplo, qué envía mi servidor web cuando pido las cabeceras asociadas a un documento cualquiera de mi página (lo que yo escribo lo pongo en negrita):
# telnet www.argo.es 80 Trying 195.53.30.2... Connected to corinto.argo.es. Escape character is '^]'. HEAD /~jcea/ HTTP/1.0 HTTP/1.1 200 OK Date: Fri, 24 Mar 2000 15:24:45 GMT Server: Apache/1.3.12 (Unix) AuthMySQL/2.20 PHP/3.0.15 Last-Modified: Tue, 15 Feb 2000 19:59:56 GMT ETag: "30c14-193f-38a9b03c" Accept-Ranges: bytes Content-Length: 6463 Connection: close Content-Type: text/html; charset=iso-8859-1 Connection closed by foreign host. #
Las cabeceras que nos responde el servidor son más o menos obvias; "Date" nos dice el momento en que se ha generado la respuesta (típicamente "ahora", a menos que tengamos cachés intermedias). "Last-Modified" nos indica la fecha de la última modificación del documento, y "Etag" es un identificador único de objeto (más sobre esto luego).
Cuando un usuario visualiza mi página, su navegador conserva una copia de la misma en el disco duro del usuario (el tamaño de caché es configurable; a medida que la caché se llena se van eliminado los objetos más antiguos). Si un tiempo más tarde el usuario vuelve a visitar mis mismas páginas, y su caché todavía conserva la copia, el navegador consultará a mi servidor para preguntarle si las páginas han cambiado o no. Si han cambiado, visualiza la versión nueva (y la guarda en la caché otra vez); si no han cambiado, visualiza la copia de la caché, que todavía está actualizada.
Físicamente el procedimiento se implementa utilizando una petición condicional:
# telnet www.argo.es 80 Trying 195.53.30.2... Connected to corinto.argo.es. Escape character is '^]'. GET /~jcea/ HTTP/1.0 If-Modified-Since: Tue, 15 Feb 2000 19:59:56 GMT HTTP/1.1 304 Not Modified Date: Fri, 24 Mar 2000 15:39:07 GMT Server: Apache/1.3.12 (Unix) AuthMySQL/2.20 PHP/3.0.15 Connection: close ETag: "30c14-193f-38a9b03c" Connection closed by foreign host. #
En este caso el servidor me indica que el objeto no ha sido modificado, por lo que puedo seguir utilizando la versión disponible en mi caché, y cierra la conexión. Si la página hubiera sido modificada, el servidor me la enviaría de nuevo completa, otra vez.
Por eso muchas veces cuando volvemos a una página que hemos visitado recientemente, los gráficos nos van apareciendo "de golpe", no poco a poco como cuando la visitamos por primera vez. Ello es debido a que el gráfico ya lo tenemos, no hay que cargarlo de nuevo, pero el navegador no lo visualiza hasta que haya contrastado con el servidor original que no ha habido ningún cambio.
¿No es genial?.
Lo ideal sería poder evitar, incluso, esas conexiones de "verificación", ya que suponen carga en el servidor y retardan innecesariamente la visualización de los datos. Y ello es perfectamente posible si podemos garantizar que un objeto web no ha sido modificado.
Supongamos que cuando el servidor nos envía un objeto web nos indica también su período de validez. Si volvemos a acceder a ese recurso antes de que haya "caducado" no necesitamos realizar una petición condicional al servidor, ya que sabemos que no ha habido cambios:
# telnet www 80 Trying 195.53.30.2... Connected to corinto.argo.es. Escape character is '^]'. HEAD /cgi-bin/conexiones HTTP/1.0 HTTP/1.1 200 OK Date: Fri, 24 Mar 2000 16:05:38 GMT Server: Apache/1.3.12 (Unix) AuthMySQL/2.20 PHP/3.0.15 Cache-Control: private Expires: Fri Mar 28 16:05:39 2000 GMT Last-Modified: Fri, 20 Mar 2000 16:05:39 GMT Connection: close Content-Type: text/html; charset=iso-8859-1 Connection closed by foreign host. #
Aquí nos aparecen dos cabeceras nuevas: "Cache-Control", que veremos más adelante, y "Expires".
En el ejemplo propuesto, se nos indica que la página fue modificada por última vez el día 20, y que es válida hasta el día 28. Si el usuario vuelve a visualizar esa página antes del día 28, su navegador le mostrará directamente la copia de la caché local, si está disponible, sin necesidad de consultar primero con el servidor origen.
Vemos, por tanto, que un uso inteligente de la cabecera "Expires" puede reducir el número de conexiones y la carga en los servidores web, al mismo tiempo que hace que la navegación del usuario sea instantanea. ¡¡La panacea!!.
Las cosas nunca son sencillas, y menos cuando hablamos de estos temas. En la sección anterior, por ejemplo, se habla de que si el servidor original no envía un "Expires" explícito, realizaremos siempre una conexión para comprobar si el objeto ha sido modificado.
En la práctica el tema es bastante más complicado.
Para reducir el tráfico y aumentar la velocidad de navegación, además de que la mayoría de los objetos web no tienen una cabecera "Expires" explícita, la mayoría de los sistemas de caché realizan un cálculo implícito sobre la "frescura" de un objeto dado. Aunque suele ser algo configurable, suele ser típico asignar una "frescura" en función de cuánto tiempo haya transcurrido desde la última modificación.
Por ejemplo, si tenemos un objeto en caché desde hace 10 días, y cuando se introdujo en la caché se sabía que había sido modificado 150 días antes, es razonable pensar que la página no ha cambiado en los 10 días transcurridos. El sistema caché puede decidir arriesgarse y entregar el objeto sin haber verificado con el servidor original. Si el objeto, en cambio, lleva en caché media hora, y cuando se capturó hacía 10 minutos que se había modificado, la caché verificará que el objeto es "fresco" antes de enviar una copia al usuario.
Obviamente esta cálculo implícito no se realiza si el objeto incluye una cabecera "Expires", por razones obvias. Pero, claro, el 99% de las páginas de Internet no incluyen una cabecera así.
Pueden verse ejemplos y sugerencias de cálculo de "frescura" en el propio documento que define el estándar HTTP/1.1 (enlace en el apéndice), sección 13.
Existe una cabecera que ya ha surgido, pero que no se ha explicado aún: "Cache-Control".
"Cache-Control" es una cabecera que puede emplearse tanto hacia el servidor como en su respuesta, con significados distintos.
Navegador -> Servidor | Servidor -> Navegador |
---|---|
|
|
La directiva "Cache-Control" es válida para aquellos sistemas compatibles con HTTP/1.1. Los sistemas HTTP/1.0, por ejemplo, no disponen de esta directiva. Se puede emplear la directiva "Pragma: no-cache", por ejemplo.
La directiva "Cache-Control" es importante y compleja, y se remite al lector interesado al propio estándar HTTP/1.1, sección 14.9.
Por ejemplo, cuando un cliente desea actualizar una página web que cree que puede haber cambiado y piensa que existe una caché intermedia que está entregando una versión anticuada, puede pulsar sobre el botón de recarga teniendo pulsada la tecla "SHIFT". Ello hace que su navegador inicie una petición con "Cache-Control: no-cache" y/o "Pragma: no-cache" (por compatibilidad), de forma tal que la caché deba recargar el objeto del servidor original.
Si no queremos que el Internet Explorer haga caché de un objeto, hay que añadirle una cabecera "Expires: -1". Lo bueno es que esta cabecera parece funcionar perfectamente también sobre Netscape y cualquier caché conocida.
Hemos visto que el rendimiento de la navegación puede mejorarse enormemente empleando de forma inteligente sistemas de caché, para lo cual es preciso una cierta cooperación entre el servidor original, y los usuarios y cachés intermedias que puedan existir. Dicha cooperación se especifica mediante el intercambio de cabeceras, como se ha indicado con anterioridad.
La cuestión ahora es ¿Cómo generar dichas cabeceras?.
Estos objetos se generan a través de algún tipo de CGI, PHP, ASP, etc., por lo que ya tenemos un punto de anclaje donde introducir las cabeceras. Es decir, si el objeto es devuelto a través de un programa, sólo tenemos que modificar dicho programa para que incluya las cabeceras que deseemos al principio del objeto.
Es importante recordar que la mayoría de los servidores web no incluyen una cabecera "Last-Modified" cuando el objeto se genera de forma dinámica, lo que casi siempre implica que el objeto no es apto para caché a menos que incluya una cabecera "Expires" explícita, por ejemplo. Asimismo, el objeto no se podrá revalidar.
La forma más sencilla, a priori, consiste en transmitir dichos objetos a través de un contenedor dinámico que introduzca las cabeceras correspondientes. Este sistema es perfectamente usable y resulta muy portable.
Pero la mayoría de los servidores web disponen de algún tipo de configuración para introducir cabeceras adicionales en la respuesta. Por ejemplo, en UNIX es muy típico el fichero ".htaccess", en el que se pueden poner cabeceras extra, entre otras muchas cosas (ver la documentación de cada servidor web; por ejemplo, Apache).
Apache tiene, por ejemplo, una configuración global para las expiraciones, sin usar el fichero ".htaccess". Se trata del módulo "mod_expires". Este módulo permite devolver una cabecera "Expires" explícita en función del tipo de objeto de que se trate. La expiración puede tomar como base tanto la fecha de la última modificación del objeto como el instante de petición. Puede consultarse la documentación correspondiente en los enlaces al final de este documento.
Más información sobre los OpenBadges
Donación BitCoin: 19niBN42ac2pqDQFx6GJZxry2JQSFvwAfS