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 BrunoEm 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!