viernes, 30 de mayo de 2008

Compresión HTTP

[Actualizado Oct'08 para corregir la parte de XML de configuración de Glassfish, el editor de Blogger me había cortado algunas cosas... :-(]

Es increíble que a día de hoy, año 2008, todavía un gran porcentaje de sitios y servicios web no tengan implementada ni la optimización más básica de todas: compresión de los contenidos servidos para aligerar las comunicaciones.

¿Por qué Google es el buscador más rápido? ¿por qué eBankinter es la banca online más rápida? Por mil cosas, y una de ellas es esta, claro. Y otra es la correcta gestión de las cabeceras de expiración de contenidos, pero eso puede que sea objeto de otra entrada... :-)

El protocolo HTTP lo soporta desde "casi siempre" y los navegadores también, incluso aquellos Netscape 4 e internet Explorer 4 de los que ya no nos acordamos y que tantos quebraderos de cabeza nos dieron hace unos añitos... Y el protocolo FTP también lo soporta (el llamado "Z Mode").

No voy a entrar en las posibilidades o particularidades, o en qué situaciones es más perjudicial que beneficioso porque al fin y al cabo consumimos recursos de procesamiento tanto en cliente como en servidor (por lo general en aplicaciones de Intranet en las que el ancho de banda es realmente alto). Pero, por lo general un fichero HTML normalito tiene una tasa de compresión con GZIP de entre 5:1 y 15:1. O lo que es lo mismo, una página de 200 KByes puede suponer que en realidad al navegador transmitamos sólo 20 KBytes. Y eso no sólo es importante en términos de visitantes unitarios, sino en términos de ancho de banda servidor en situaciones de concurrencia importante.

El protocolo HTTP, por ejemplo, define un diálogo muy sencillo entre el cliente (navegador) y el servidor, para el primero informe al segundo de sus "capacidades" y el segundo informe al primero de si el contenido está comprimido o no. Se hace a través de las cabeceras HTTP. Por ejemplo:

Header en la petición:
Accept-Encoding: gzip, deflate
Headers en la respuesta:
Vary: Accept-Encoding
Content-Encoding: gzip

¿Mola? Pues es trivial de implementar. Tanto el IIS como Apache Web Server (a través de mod_deflate o mod_gzip) como Glassfish, Tomcat, Orion, etc, etc, lo soportan.

Algunos ejemplos sencillos.

En Tomcat basta con añadir al tag <Connector> del fichero server.xml:
compression="on"
compressableMimeType="text/html,text/xml,text/css,text/javascript,text/plain,application/x-javascript,text/unknown,text/*"

En Glassfish basta con añadir lo equivalente en el domain.xml, dentro de las secciones <http-listener> que deseemos:
<property name="compression" value="on"/>
<property name="compressableMimeType" value="text/html,text/xml,text/css,text/javascript,text/plain,application/x-javascript,application/force-download,application/pdf,text/unknown,text/*"/>
En Apache es también trivial; por ejemplo en Apache 2.2 con mod_deflate la configuración sería:
AddOutputFilterByType DEFLATE text/html text/xml text/css text/javascript text/plain application/x-javascript application/force-download application/pdf text/unknown text/*
En iPlanet idem, además se permite tanto la carga de archivos "precomprimidos" y la compresión al vuelo. Para la compresión dinámica:
Output fn="insert-filter" filter="http-compression" type="text/*" vary="on"
Output fn="insert-filter" filter="http-compression" type="application/x-javascript" vary="on"
Output fn="insert-filter" filter="http-compression" type="application/force-download" vary="on"
Output fn="insert-filter" filter="http-compression" type="application/pdf" vary="on"
Y para soporte a archivos precomprimidos en iPlanet (deben existir las dos versiones, por ejemplo "prueba.js" y "prueba.js.gz"). Aquí habrá que hacerlo por cada extensión o similar (no es un filtro de salida sino un procesamiento previo, por ejemplo para aplicarlos a ficheros .js:
<Object ppath="*/*.js">
    PathCheck fn="find-compressed" check-age="on" vary="on"
</Object>


Hay unas cuantas opciones que considerar (como el tamaño mínimo de un fichero para que lo comprima, o si debe ignorar algunos navegadores) e instrucciones a los proxy caches para indicarle que este contenido es una variante (es decir que envíe la cabecera Vary: Accept-Encoding), pero básicamente ahí está...

Además de compresión por software, se puede utilizar hardware de aceleración especializado para ello, pero eso también será objeto de otra entrada...

Algunos links de interés:

3 comentarios:

Juanma dijo...

Interesante tema.

No obstante no encuentro información al respecto de como configurar ( o implementar) dicha solución para Bea Weblogic.

¿Es posible?

Saludos

serverperformance dijo...

Claro que es posible.

- Si tienes un apache delante como proxy inverso, instala y configura mod_deflate para que sea el servidor web quien se encargue de estas tareas tanto para el contenido estático como el dinámico.

- En caso contrario, utiliza un servlet filter y configúralo para que se ejecute en tu aplicación web, en uno o varios servlets o JSPs, o a nivel global para todo el servidor de aplicaciones. Hay cientos por ahí, basta con que busques en google "gzip servlet filter". Yo hice uno propio hace un porrón de años, es sencillo en realidad pero no merece la pena salvo que quieras entrenarte, en caso contrario busca uno opensource... Si tienes mucha concurrencia, es posible que esta opción sea peor que la anterior.

- No me suena que Bea/Oracle hayan introducido alguna opción "nativa" empaquetada en el servidor de aplicaciones, así que tendrás que ir a una de las dos opciones anteriores.

Juanma dijo...

No tengo Apache y trabajo directamente sobre BEA ( que por cierto me está decepcionando en demasia).
Ya tenia pensado el tema del servlet, pero visto el articulo no estaba de mas preguntar si sonaba algo.

Gracias

pd: muy bueno tu blog, me lo pongo en favoritos( a la de ya!!)