viernes, 8 de agosto de 2008

Compresión de contenidos vía Deflate o Gzip

Hola, en esta ocasión explicaré un poco este tema de cómo comprimir los contenidos generados por nuestras páginas.

El objetivo a lograr es reducir el peso del contenido HTML generado por nuestras páginas, de ésta forma se pretende aumentar la performance de nuestra aplicación al reducir los tiempos de espera usados para que se dibuje la página en el cliente y el transporte del contenido entre el cliente y el servidor sean menores.

Un beneficio directo de esta técnica es alivianar la carga existente para la red de la empresa si nuestra aplicación funciona sobre una intranet y en caso contrario (internet) mejorar el tiempo de descarga del contenido para los clientes. Otro beneficio indirecto es que se aprovecha la capacidad de los clientes al usar la descompresión automática de contenidos soportada por la mayoría de los clientes (browsers) actuales.

Desde la versión de IIS 6 en adelante es posible habilitar la compresión automática de contenidos, para habilitarla sobre el IIS 6 podemos ver el siguiente ejemplo en inglés:

http://www.microsoft.com/technet/prodtechnol/WindowsServer2003/Library/IIS/502ef631-3695-4616-b268-cbe7cf1351ce.mspx?mfr=true

Y también tenemos éste ejemplo en español:

http://www.mygnet.net/articulos/iis/comprimir_archivos_http_en_iis_6_dot_0.271

El problema viene cuando nuestro ambiente de producción no tiene habilitada la compresión automática de contenidos, esto puede pasar por diferentes razones como por ejemplo que en la misma máquina estén funcionando otras aplicaciones además de la nuestra y las otras aplicaciones no sean compatibles con ésta compresión de contenidos o también puede pasar que simplemente no tengamos acceso a las máquinas de producción.

Para revisar si nuestra aplicación en ambiente producción (suponiendo que es visible desde internet) tiene habilitada la compresión automática de contenidos podemos usar la siguiente aplicación donde pondremos la dirección de nuestra aplicación:

http://www.aspnetresources.com/tools/httpcompression.aspx

Si el resultado del análisis nos entrega lo siguiente:

Content type: UTF-8
Page size: 46,775 bytes
Served uncompressed

Significa que no está habilitada la compresión automática de contenidos por lo tanto podríamos intentar usar la compresión manual.

Para implementar la compresión manual de contenidos en ciertas páginas podemos hacer lo siguiente, primero crearemos un método que nos indicará si el cliente soporta la compresión, primero probaremos con el método deflate y luego con gzip, básicamente porque el método deflate se comporta mejor que gzip en cuanto a tiempo de respuesta:

/// <summary>
/// Determines if GZip is supported
/// </summary>
/// <returns></returns>
protected static bool IsGZipSupported()
{
    string AcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"];
    if (!string.IsNullOrEmpty(AcceptEncoding) &&
 (AcceptEncoding.Contains("deflate") || AcceptEncoding.Contains("gzip")))
return true;
    return false;
}

Como ya tenemos el método que nos indicará si la compresión es soportada a continuación creamos el método que usaremos para realizar la compresión propiamente tal:


/// <summary>
/// Sets up the current page or handler to use GZip through a Response.Filter
/// IMPORTANT: 
/// You have to call this method before any output is generated!
/// </summary>
protected static void GZipEncodePage()
{
    if (IsGZipSupported())
    {
        HttpResponse Response = HttpContext.Current.Response;
 
        string AcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"];
        if (AcceptEncoding.Contains("deflate"))
        {
            Response.Filter = new System.IO.Compression.DeflateStream(Response.Filter,
                                      System.IO.Compression.CompressionMode.Compress);
            Response.AppendHeader("Content-Encoding", "deflate");
        }
        else
        {
            Response.Filter = new System.IO.Compression.GZipStream(Response.Filter,
                                      System.IO.Compression.CompressionMode.Compress);
            Response.AppendHeader("Content-Encoding", "gzip");
        }
    }
}

Lo ideal es tener este par de métodos en alguna clase base o de utilitarios de manera que podamos invocarlos de la forma más fácil que podamos, en éste caso ambos métodos los puse en una clase base y como son estáticos basta con llamar al método que realiza la compresión en el Page_Load de la página donde lo vamos a usar:


protected void Page_Load(object sender, EventArgs e)
{
    GZipEncodePage();
if (!IsPostBack)
    {

Como restricción para usar esta técnica es que debemos usar la llamada al método de compresión (en nuestro caso GZipEncodePage) en la primera línea del método Page_Load de la página donde lo usemos.

Una buena razón para usar la compresión manual es que podremos realizar una compresión selectiva de las páginas a las que queremos aplicarles compresión.

Debemos considerar que el uso de compresión crea un consumo extra de tiempo de CPU a la hora de usar ésta técnica aunque es mínimo y realizado por el cliente si es que lo soporta, por lo tanto lo ideal es implementar la compresión en aquellas páginas que consideremos como buenas candidatas para ésta técnica, un buen ejemplo son aquellas páginas que generan mucho contenido HTML.

Para ejemplificar mejor lo que podremos conseguir con ésta técnica podemos ver a continuación una página de ejemplo sin la compresión habilitada:

Prueba_Gzip-antes-mini

Como podemos ver el contenido HTML de la página es de 54.6K con un tiempo de respuesta de 12.04s.

Ahora veremos los resultados de la misma página pero con la compresión habilitada:

Prueba_Gzip-despues-mini

Como podemos ver pasamos de 54.6K a 13.2K de contenido HTML con un considerable ahorro de datos transportados entre el cliente y el servidor y aún mejor el tiempo de respuesta que bajó de 12.04s a 5.917s, si imaginamos resultados similares con muchos clientes concurrentes podemos hacernos una idea de las mejoras que podremos obtener con nuestra aplicación.

Como mencioné anteriormente si ésta técnica es bien usada donde corresponde puede ayudar a alivianar un poco la carga de datos entre el cliente y el servidor pero no debe ser interpretada como una fórmula mágica.

Como referencia usé este artículo de Rick Strahl (MVP):

http://www.west-wind.com/weblog/posts/10564.aspx

Ojalá les sirva, saludos.


Pd.: Sorry por el código si se ve mal formateado.

No hay comentarios.: