Optimizando el rendimiento de nuestro sitio web

Tras leer el interesante libro de Steve Souders High Performance Web Sites, Essential Knowledge for Front-End Engineers  me dispuse a realizar tareas de Optimización en una aplicación web con alto tráfico. El siguiente artículo pretende dar cuenta de algunas prácticas que desarrollé y de cómo éstas me permitieron una carga más veloz al tiempo en que liberaron a mi servidor de una cantidad considerable de Requests.



 
Introduccion a los HTTP Requests

 
Para comenzar, expliquemos brevemente qué son los HTTP Requests y qué sucede en nuestro servidor cuando un visitante solicita, por ejemplo, nuestro Homepage ingresando la url del sitio en su navegador y dando Enter. Usando palabras de Steve Souders:

 
?HTTP es un protocolo cliente/servidor constituido por Solicitudes y Respuestas. Un navegador envía una solicitud HTTP a una url específica y el servidor que almacena los contenidos de esa URL responde, también mediante HTTP?.


 
Explicar en detalle cómo funciona el protocolo HTTP requeriría un artículo aparte, pero es altamente recomendable comprender el funcionamiento de este protocolo dado que es el encargado de negociar la interacción entre nuestros visitantes y nuestro servidor.
 
A nuestros fines y simplificando muchísimo el proceso, diremos que cuando un visitante escribe nuestra URL e ingresa a nuestro sitio el navegador envía un Request inicial para acceder a nuestro HTML. Si todo funciona bien, el servidor responde OK (HTTP/1.1 200 OK).
 
Una vez que el navegador ha obtenido una respuesta del tipo OK al DOM de nuestra web, procede a recorrer nuestro código en busca de los elementos que constituyen nuestra web. Cada vez que se encuentra un elemento invocado (por ejemplo, una hoja de estilos, un script js o una imagen) el navegador vuelve a enviar un Request, que el servidor deberá responder al tiempo en que devuelve el contenido solicitado.
 
Explicado esto, es justo decir que para que nuestro sitio esté 100% disponible al visitante, el servidor debe responder a tantos Requests como componentes tenga nuestro sitio + 1. Esto, como podrán imaginarse, es lo que determina la velocidad con que nuestro sitio responderá. Si tenemos llamados a 3 hojas de estilo, 3 Scripts js, 5 imágenes llamadas explicitamente vía <img> nuestro servidor deberá responder a 1 Request inicial + 11 Requests de contenidos + Requests que puedan provenir de imágenes invocadas vía CSS en las hojas: cada background: url(path/to/image), es 1 Request más a responder.
 
Antes de continuar, demos un vistazo a qué sucede con nuestro sitio cuando alguien solicita una página haciendo uso de una excelente herramienta Online llamada WEBPAGETEST. Introduzcan allí su URL y estudien el informe.



 
Economizando los HTTP Requests

 
La primera medida a tomar para Optimizar la carga de un sitio web es reducir los Requests que nuestro servidor deberá responder. Para esto, disponemos de muchos métodos. Siguiendo a Souders veremos algunos:

 
1- Usar CDNs

 
Las CDNs o Content Delivery Networks son redes de servidores distribuídos geográficamente que sirven contenido a un visitante utilizando el servidor más próximo a él en función de su IP. El ejemplo más común es la CDN de Google API que nos permite invocar librerías o scripts desde sus servidores ahorrándole a nuestro Host la respuesta y servido de ese contenido (hay que aclarar, aunque obvio, que nuestro servidor al responder OK a un request luego debe presentarlo y esto implica uso de nuestro Bandwith).
 
Mi sitio trabaja con Jquery y Jquery UI (que a su vez incluye estilos e imágenes del theme que uno use). Todo este contenido puede ser descargado de los sitios de Jquery y Jquery UI respectivamente y puesto a funcionar en nuestro servidor, pero: ¿qué sentido tiene esto si podemos llamar los 2 paquetes y sus componentes desde la CDN de Google?

 - http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/smoothness/jquery-ui.css
- http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js
- http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js
 
Llamando a Jquery y Jquery UI con sus estilos desde Google le ahorraremos una cantidad considerable de HTTP Requests a nuestro Servidor (especialmente por los estilos del theme de JqueryUI y sus imágenes).



2- Minificar y unir

 

 
La minificación es la práctica mediante la cual se remueven espacios innecesarios en un script para hacerlo más liviano (Si miran los links de ls CDN de Google para Jquery podrán ver un ejemplo). La minificación es utilizada especialmente con scripts JS y existen múltiples sitios que permiten minificar un script online ahorrándonos la tediosa tarea de hacerlo manualmente (http://jscompress.com).
 
El pro de esta práctica: archivos más livianos lo cual equivale a menor ancho de Banda. La contra: los archivos se convierten  en una línea críptica de código muy poco legible por lo que es recomendable conservar el archivo original para introducir futuros cambios y luego minificar y subir.
 
La minificación nos ahorrará ancho de banda achicando el tamaño de los scripts pero no nos resuelve en nada la cantidad de HTTP Requests. Para esto una solución muy sencilla: unir scripts de modo de llamar la menor cantidad posible. Muchas veces sucede que en el proceso de desarrollo y manteniemiento de un sitio web agregamos nuevas funcionalidades y simplemente pegamos en el head de nuestra web las llamadas a los nuevos Scripts JS y estilos CSS: todo funciona bien y así se queda.
 
Si tu sitio está llamando más de 2 scripts desde tu Servidor o invoca más de 1 hoja de estilos es tiempo de preguntarte si no podrás unir los scripts en 1 solo fichero. Te sorprenderá saber que fusionar 3 hojas de estilo en 1 darán un archivo CSS que pesará menos que las 3 hojas sumadas al tiempo en que ahorrás 2 Responses al Servidor.



 
3- Trabajar con Sprites CSS

 
Esto es tan simple como suena. Si tu hoja de estilos llama 5 imágenes distintas vía background: url(path/to/image) entonces tu hoja de estilo le está diciendo al Navegador que envíe 5 Requests para obtener las imágenes.
 
La solución, nuevamente, es muy simple: junta las 5 imágenes en 1 sola, llamala 1 vez con background: url(path/to/image) y luego utiliza background-position para localizar cada una de las 5 imagenes en tu Sprite. El resultado: 4 HTTP REQUESTS menos y la sorpresa de ver que la imagen conteniendo las 5 imágenes es más liviana que la suma de las 5 por separado.
 
Puedes armar tus Sprites con Photoshop o bien probar alguna herramienta para armar sprites Online como CSS Sprite Generator en que subes un zip con todas las imágenes que usas en tu CSS por separado y obtienes una imagen única lista para ser usada.



 
Trabajando con Amazon S3

 
Finalmente y dándole sentido al título de este artículo usaremos el servicio de almacenamiento de Amazon (Amazon Simple Storage Service o S3). Se trata de una solución económica (uno paga por lo que usa) y fiable para almacenar archivos de forma transparente para nuestros usuarios y visitantes. Amazon S3 es altamente recomendable para manejar una gran cantidad de archivos sin utilizar el Bandwith de nuestro servidor: imágenes, textos, scripts, css, etc.
 
Si, por ejemplo, el sitio web que queremos optimizar permite a los visitantes crearse una cuenta y subir una imagen de usuario, el uploader enviaría esta imagen a S3 y la mostraría luego cada vez que sea necesario desde el Bucket que creamos en S3 (en mi caso no sólo permito a mis usuarios cargar un avatar sino que éstos pueden subir documentos de hasta 10MB cada uno). Amazon S3 es, simplemente la solución correcta: nos ahorra los HTTP Requests (que son enviados a sus servidores), nos ahorra una cantidad de Bandwith que dependerá de qué clase de uploads permitamos y, por último y totalmente desvinculado del tema Optimización, cubre un potencial agujero en la seguridad anulandoló por completo al enviar todo a S3 de suerte tal de que si alguien se las ingenia para falsear el sistema de control de extensiones de archivos o sube una shell como la C99 con formato .txt y  encuentra un modo de renombrarla habrá subido un archivo .php a un servidor que no interpreta PHP: la shell es obsoleta y el atacante ha perdido su tiempo.
 
Les dejo un link a una clase en PHP para gestionar Subidas a Amazon S3 llamada Amazon S3 PHP Class. Es una clase interesante para comenzar aunque pueden encontrarse uploaders con progress bar y demás características que funcionan con S3.
 
Yo he dado un paso más en el uso de S3: subi hojas de estilos, imágenes usadas por ellas y scripts de js. En sintesis, mi servidor responde al primer Request con 200 y luego el navegador del visitante comienza a enviar Requests a la CDN de Google y a S3. Creanmé que carga rápido!

 
¿Pero y si Amazon S3 se cae?

 
Esta es una interesante pregunta. Mi respuesta es la siguiente: tanto en estos menesteres como en otros, siempre está preparado para que las cosas fallen con un plan B. En lo que refiere a los ficheros JS, CSS e imágenes asociadas a los estilos no tengo nada en S3 que no tenga en mi servidor. Simplemente uso la versión de S3 pero sólo cambiando un valor en el fichero de configuracion de mi aplicación todo lo que se llama de S3 pasa a llamarse de local.
 
En lo que refiere a archivos pesados de usuarios o de la propia aplicación (Documentos, Videos, Imágenes o lo que sea que uno haya delegado) sería irresponsable pensar que S3 nunca fallará por lo que es recomendable tener una réplica en otro servidor y un mecanismo similar al descripto antes para que los archivos que se llaman desde S3 se llamen desde la réplica. Cómo mover los archivos de S3 al servidor réplica es un tema a conversar dado que seguramente existen programas que permitan esto o, inclusive, uno podría codear su propio script que mantenga sincronizados los archivos.
 
Yo he optado por una solución de otra clase: uso 2 buckets en 2 locaciones distintas (USA y Singapur) y sincronizo los contenidos con Cloudberry Explorer for Amazon. Mi circuito diario es: Bucket 1 -> HDD Local -> Bucket 2 ,de modo de tener una copia de todos los archivos en mi PC y en el segundo Bucket (en caso de que el servicio completo de Amazon S3 vuele por el aire y ningún servidor responda).



 
 Conclusiones

 
El libro de Steve Souders High Performance Web Sites, Essential Knowledge for Front-End Engineers es de lectura recomendable y encontrarán en el muchas buenas prácticas para Optimizar la Performance de sus sitios que aquí no se han tratado.
 
Nos hemos limitado a tomar algunos de sus consejos y a usar CDNs y S3 para servir contenido. Esto le hace a nuestro servidor la vida más facil, pero sin las prácticas de Minificación, Unión de Scripts y Hojas de Estilo y Uso de Sprites aquí mencionadas, aunque llamemos los componentes de nuestra web desde S3 tendremos la misma cantidad de Requests (con Responses de Google o S3, lo cual puede mejorar el tiempo de las respuestas pero no las reducirá en número).
 
Dicho esto, queda claro que una correcta optimización requiere necesariamente la reducción de Requests enviados por el navegador. Antes de optimizar les recomiendo correr el test Online de WEBPAGETEST y verificar la cantidad de REQUESTS enviados para tener un parametro con que medir el éxito de la Optimización.




Optimizando el rendimiento de nuestro sitio web con AmazonS3
Optimizando el rendimiento de nuestro sitio web
15 Puntos Score: 5/10
Visitas: 896 Favoritos: 10
Ver los usuarios que votaron...
6 Comentarios Optimizando el rendimiento de nuestro sitio web
Muy interesante amigo, thank's  
Interesante.Gracias
q buena ta la info graxias necesitaba algo asi  
Cita kraken89: Mostrar
Gracias a vos capo.
Para dejar un comentario Registrate! o.. eres ya usuario? Accede!