Desenvolvimento - C#

Usando modules e compressão para turbinar suas requisições web

Veja nesse artigo como simples passos podem levar o desempenho de suas aplicações as alturas! Com o uso de HttpModules e compressão podemos fazer milagres com certas aplicações, aprenda esta técnica e tenha um diferencial entre os concorrentes!

por Cleyton Bruno



Em tempos de aplicações web extremamente complexas muitas vezes nos perguntamos se otimizamos ao máximo a velocidade de carregamento. Quantos kb tem os arquivos javascript? E o css? E a própria página aspx? Bom, hoje pretendo ajudá-los com uma coisa bem simples, mas que vai fazer muita diferença. Vou falar sobre a compressão das requisições.

Poucos sabem, mas as requisições podem ser comprimidas através de gzip ou deflate. Isso garante que aquela sua página com css e javascript, juntos, que demoram 700 ms para carregar, passem a demorar apenas 200ms... Isso de maneira transparente, com a DLL pronta e uma linha no web.config você garante a satisfação do seu cliente e o diferencial entre os concorrentes. Vamos a DLL então.

Para capturar as requisições vamos usar um módulo. Começaremos criando uma aplicação do tipo Class Library chamada HttpUtils. Criamos então uma classe chamada CompressionModule. Vou mostrar a seguir os códigos comentados.

//Esse método vai receber o contexto da requisição e comprimir o conteúdo se possível

private void CompressResponse(HttpContext context)

{

//Verifica se na requisição existe um cabeçalho dizendo "Sim, aceito codificação"

string AcceptEncoding = context.Request.Headers["Accept-Encoding"];

//Caso aceite codificação, verifica se nas codificações aceitas existe gzip ou deflate

if (!string.IsNullOrEmpty(AcceptEncoding) &&

(AcceptEncoding.Contains("gzip") || AcceptEncoding.Contains("deflate")))

{

//Se tiver gzip, configura a reposta para comprimir o conteúdo e coloca no

//cabeçalho que o conteúdo está comprimido com gzip

if (AcceptEncoding.Contains("gzip"))

{

context.Response.Filter = new GZipStream(context.Response.Filter,

                                                           CompressionMode.Compress);

context.Response.AppendHeader("Content-Encoding", "gzip");

}

//Se tiver deflate, configura a reposta para comprimir o conteúdo e coloca no

//cabeçalho que o conteúdo está comprimido com deflate

else

{

context.Response.Filter = new DeflateStream(context.Response.Filter,

                                                           CompressionMode.Compress);

context.Response.AppendHeader("Content-Encoding", "deflate");

}

}

}

Lembrando que para o CompressionMode, GZipStream e DeflateStream serem reconhecido, o namespace System.IO.Compression deve ser incluído:

using System.IO.Compression;

Resumindo, o que o método CompressResponse faz é verificar se é possível enviar a resposta comprimida. Caso seja possível ele configura a resposta para que ela seja comprimida ou com gzip ou com deflate.

Agora vamos ao módulo.

//O módulo, por padrão, implementa a interface IHttpModule, que possui

//os métodos Dispose e Init.

public class CompressionModule : IHttpModule

{

//Como não precisamos fazer nada no método Dispose para este módulo,

//vamos deixar em branco

public void Dispose() { }

//No init adicionamos um método para tratar o evento PostReleaseRequestState.

//Esse é o melhor evento para fazer a compressão. Nos testes que fiz com os outros

//eventos verifiquei que eles ocorriam cedo ou tarde demais para que o melhor

//desempenho fosse alcançado

public void Init(HttpApplication context)

{

context.PostReleaseRequestState += new EventHandler(context_PostReleaseRequestState);

}

//Aqui tratamos o evento

void context_PostReleaseRequestState(object sender, EventArgs e)

{

//Convertemos o sender para o tipo HttpApplication

HttpApplication app = sender as HttpApplication;

                       

//Se ele for do tipo System.Web.UI.Page (aspx), css ou javascript, então

//podemos fazer a compressão

if (app.Context.CurrentHandler is System.Web.UI.Page || app.Request.Path.Contains("css") || app.Request.Path.Contains("js"))

{

CompressResponse(app.Context);

}

                       

//Fim da resposta

app.Context.Response.End();

}

}

Lembre-se que o método privado CompressResponse deve ser colocado dentro do módulo, só foi retirado aqui para não comprometer o entendimento do código.

Não podemos esquecer também que, para que a nossa possa reconhecer os tipos HttpApplication e System.Web.UI.Page é preciso adicionar uma referência para System.Web.

Por último, vamos adicionar a nossa DLL a uma aplicação web. Adicionamos a referência a nossa DLL e colocamos no web.config.

No caso de IIS6 colocamos:

<configuration>

<system.web>

<httpModules>

<add name=CompressionModule type=HttpUtils.CompressionModule, HttpUtils />

</httpModules>

</system.web>

</configuration>

No caso de IIS7 colocamos:

<configuration>

<system.webServer>

<httpModules>

<add name=CompressionModule type=HttpUtils.CompressionModule, HttpUtils />

</httpModules>

</system.webServer>

</configuration>

O que fizemos aqui é registrar o módulo em nossa aplicação. O método init de cada módulo registrado é chamado quando a aplicação inicializa. No método init, estamos adicionando um método para tratar o evento PostReleaseRequestState. Esse evento foi o escolhido depois de diversos testes. Verifiquei que qualquer outro evento era muito cedo ou muito tarde para fazer a compressão. Não é que não funciona, é que pode dar problemas de compatibilidade, como com algums frameworks Ajax por exemplo.

Uma vez que é feita alguma requisição, uma série de eventos são disparados, até que chega a vez do PostReleaseRequestState. Quando ele é disparado nosso método é chamado. Esse método verifica se o arquivo requisitado é do tipo System.Web.UI.Page, que normalmente são os aspx, se é css ou se é js(Java script). Caso seja, ele tenta comprimir a resposta e então a finaliza, enviando o arquivo para o cliente comprimido quando tem sucesso.

Isso tudo de modo praticamente transparente, já que uma vez que temos a DLL na mão basta referenciar em seu projeto e adicionar uma linha no web.config. Desempenho a preço de banana!

Performance em aplicações web é um item de extrema importância que não deve ser negligenciado nunca. Seguindo os poucos passos deste artigo podemos melhorar, e muito, a qualidade da navegação do usuário. Quanto maior a aplicação mais perceptíveis ficam os ganhos.

Espero ter clareado as idéias de vocês para novas possibilidades. Que este artigo abra a mente de vocês para a possibilidade de compressão das requisições e para o uso de módulos para diversas outras funcionalidades. As possibilidades são infinitas!

Estou aberto a dúvidas, críticas e sugestões. Obrigado pelo seu tempo e até a próxima!

Cleyton Bruno

Cleyton Bruno